월 초에 포스팅한 내용이지만, 약간 내용을 추가해보는 의미로 글 하나 남기기.
intel TBB 2.2 부터는 Windows 환경이나 linux 환경에서 intel TBB를 써서 기본 할당자(malloc/free, 그리고 new/delete)를 덮어 쓸 수 있게 되었다.
그래서 어떻게 하는지 + 약간의 성능 비교를 해봤다.
프로그래머가 직접 malloc/free 혹은 new/delete를 덮어 쓰는 좀 안 이쁜 방법말고, hoard allocator 같은 방법을 제공할 것 같아서 새로 추가된 헤더들을 뒤져보니 이런 녀석이 있다. ((win32 용을 집에서 쓰는 데스크탑에 받아서 설치한거라 Win32 용 표현(?)만 있음에 주의))
그냥 link 시점에 malloc/free 부분을 해당 lib의 그것으로 치환하고 실제 실행 시점에 호출되게 바꾸는 형태 (debugger를 붙여보니 tbbmalloc_proxy.dll 을 통해서 동작하는걸 확인할 수 있었다)
tbb/tbbmalloc_proxy.h (GNU GPL v2)
Replacing the standard memory allocation routines in Microsoft* C/C++ RTL
(malloc/free, global new/delete, etc.) with the TBB memory allocator.Include the following header to a source of any binary which is loaded during
application startup#include “tbb/tbbmalloc_proxy.h”
or add following parameters to the linker options for the binary which is
loaded during application startup. It can be either exe-file or dll.For win32
tbbmalloc_proxy.lib /INCLUDE:”___TBB_malloc_proxy”
win64
tbbmalloc_proxy.lib /INCLUDE:”__TBB_malloc_proxy”
이 헤더를 include 하고나서 다음과 같이 간단한 — 10bytes~41 bytes 정도의 메모리를 10만 번 할당하고 해제하는걸 반복하는 – 코드를 짜서 내 데스크탑에 코어 수(=2)에 맞게 띄워서 돌려봤다.
const int N = 100000; const int M = 1000; unsigned _stdcall testProc( void* arg ) { vector<BYTE*> allocs; allocs.reserve( N ); for( int j = 0; j < M; ++j ) { for( int i = 0; i < N; ++i ) allocs.push_back( new BYTE[10 + rand() & 31] ); for( int i = 0; i < N; ++i ) delete [] allocs[i]; allocs.clear(); } return 0; }
대략 #include “tbbmalloc_proxy.h” 한 것과 안 한 것이 각각 7.10s, 16.5s 정도의 시간이 걸렸다. 2 배가 넘는 수행 시간 차이 -_-;
물론 이 정도로 무작정 메모리를 할당하고/해제하지는 않겠지만, 작은 양의 메모리(크면 차이가 좀 더 줄어들 수 있다)를 반복해서 복수의 스레드가 경쟁적으로 할당/해제할 경우, 멀티스레드에 최적화되지 않은 CRT의 malloc/free는 성능이 심히 떨어질 수 있다. 반면에 scalable_alloc / free의 형태로 구현된 intel TBB는 멀티스레드 최적화를 우선시했기에 두 스레드가 경쟁해도 좋은 성능을 보여준다.
+ intel TBB 라이센스를 확인해봤는데, runtime에 dynamic-link만 하는 경우에는 GPL v2.에 대해서도 예외를 적용한다고 한다(물론 TBB만의 얘기다). 그래서 commercial support 없이 클라이언트 코드에서 dynamic-link해서 쓰는 경우에는(.so나 .dll 등) 라이센스를 구입하지 않고도 써도 되는 듯 하다. ((물론 GPL이 적용되도 서버는 배포할 일이 없으니 그냥 쓰면 되지만)) 그래서인지 EPIC의 Unreal 3 엔진에 intel TBB가 통합되어 배포되기 시작했다.
약간 딴 얘기지만, 예전에 간단하게 측정해보니까 윈도우즈 비스타에서 기본 메모리 할당자로 채택된 LFH도 멀티쓰레드 환경에서 꽤나 쓸만한 성능을 보여주는 것 같습니다. 측정하기에 따라서는 Hoard보다도 나은 경우도 있더군요. 이야기 들어보니 Lock-free하게 구현이 되었다던데…
XP에서도 명시적으로 사용 여부를 결정해주면 쓸 수 있으니 윈도우즈 플랫폼이라면 이 쪽을 고려해보는 것도 좋을 듯.
Windows Vista Ultimate(x64) 에서 테스트했기 때문에 (아마도) LFH가 기본으로 사용되었을 것입니다. (그냥 new/delete했을 때)
intel scalable allocator의 경우엔 lock-free 보다는 per-thread-storage(Thread Local Storage)를 써서 아예 락이 무의미한 구현을 많이 쓰기 때문에 빠른 거라서 싱글 스레드에선 오버헤드떄문에 아마도 LFH보단 느리고, 이번처럼 (메모리에 대한) 경쟁이 많은 시나리오에서는 좋은 성능을 보여준듯합니다.
저 EPIC 공지에서 중요한건 “64비트 & 에디터”
언리얼에서 직접 만들어서 쓰는 메모리 관리자가 32비트만 지원했습니다. 근데 “에디터”에서 메모리를 미친듯이 쓰다보니 일단 64비트로 포팅을 했고, 메모리 관리자는 다시 만들어야 하는 수준이라 기본 malloc, free를 쓰고 있었지요. TBB를 걍 붙여준다니 에픽에서 우왕ㅋ굳ㅋ 했을 것 같네요.
언리얼 에디터가 (지나가면서 뒤에서 본거지만) 무진장 뽀대나던데 메모리도 무진장(…)쓰나요 -_-;;;;
intel TBB를 붙여주는건 사실 라이센스 살펴본걸로는 그냥 붙여서 배포해도 상관은 없어 보이던데(….). 뭐 이걸로 양쪽의 마케팅 효과라거나.
게임에서 쓰는 리소스가 커질 수록 메모리도 많이 쓰게 되지요. GoW1의 경우는 2GB에서 충분했지만 GoW2에서는 2GB가 간당간당했었던듯. 32비트 컴파일할 때 옵션 줘서 레벨디자이너들은 64비트 머신에서 3GB로 작업했던거 같아요.
GoW2 art asset이 어떻길래 -_;;;;;;
3GB <- 이건 유저 영역 3GB 주고 쓴단 얘기죠? 근데 이건 뻘소리지만 32bit에서도 NTLDR 에 옵션 잘 줘서 3G로 쓰는게 있지 않나요(잘 모르겠지만;;; )
하지만 역시 먼저 붙이자고 꼬신건 Intel이라는데 백원 걸겠습니다. :-P
인텔이 게임 업계를 자주 들여다보는 듯하긴해요. TBB 쪽 포럼글 봐도 그런 얘기가 꽤 있고 -_-;
Scientific Computation 말고 TBB/concurrent programing 제일 많이 하는 타겟을 여기로 잡는듯함;
윈도우 7에서 LFH을 켠 상태로 테스트로 사용하신 코드를 이용해 둘 다 비교를 해봤는 tbb쪽이 훨씬 빠르더군요.
LFH은 평균 24초가 나왔고 TBB는 평균 10초를 기록했습니다. 코어수가 4개라서 스레드를 4개 만들고 테스트를 해봤습니다.
제가 수행한 테스트도 Windows Vista에서니 LFH에서 차이가 2배 정도였을테지만, 역시 코어 수가 늘어나면 메모리 경쟁이 더 심해지니 tbb allocator가 성능이 더 좋아지긴하네요;
TBB 적용해서 테스팅을 해보니 역시 훨씬 빠르네요. 하긴 빠르지 않다면 따로 메모리 할당자를 만들어서 배포할 이유도 없긴 하겠지요. 흠.
다만 저렇게 모든 경우에 대해 메모리 경쟁이 일어나는 케이스 말고 평균보다 좀 심한 정도에서는 얼마나 차이가 날지 궁금하네요. 이런 건 모델링하기가 어려우니 실제 어플에 적용을 해봐야 알 것 같긴 한데…
저거보단 무조건 차이가 적겠죠(…). 사실 실제 응용에선 메모리 할당량이 뻔하면 미리 할당해놓고 시작하는 경우도 많고요.
모델링하기 어려운…편인건 맞는데 실제로 저걸 프로파일링해서 테스트하는 방법이 없는건 아니긴 합니다. 다만 완전히 실제랑 같게는 잘 안될 뿐이지만;;;
[…] 이전 TBB 의 lock free 컨테이너들에 대한 동시성 테스트를 진행하면서 멀티 스레드에 최적화된 메모리 할당자를 사용하지 않을까하는 맘에 검색을 해봤더니 역시나 있더군요. (http://rein.kr/blog/archives/1817) […]