intel TBB를 써서 메모리 할당하기

월 초에 포스팅한 내용이지만, 약간 내용을 추가해보는 의미로 글 하나 남기기.

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가 통합되어 배포되기 시작했다.

Jinuk Kim
Jinuk Kim

SW Engineer / gamer / bookworm / atheist / feminist

Articles: 935

13 Comments

  1. 약간 딴 얘기지만, 예전에 간단하게 측정해보니까 윈도우즈 비스타에서 기본 메모리 할당자로 채택된 LFH도 멀티쓰레드 환경에서 꽤나 쓸만한 성능을 보여주는 것 같습니다. 측정하기에 따라서는 Hoard보다도 나은 경우도 있더군요. 이야기 들어보니 Lock-free하게 구현이 되었다던데…

    XP에서도 명시적으로 사용 여부를 결정해주면 쓸 수 있으니 윈도우즈 플랫폼이라면 이 쪽을 고려해보는 것도 좋을 듯.

    • Windows Vista Ultimate(x64) 에서 테스트했기 때문에 (아마도) LFH가 기본으로 사용되었을 것입니다. (그냥 new/delete했을 때)
      intel scalable allocator의 경우엔 lock-free 보다는 per-thread-storage(Thread Local Storage)를 써서 아예 락이 무의미한 구현을 많이 쓰기 때문에 빠른 거라서 싱글 스레드에선 오버헤드떄문에 아마도 LFH보단 느리고, 이번처럼 (메모리에 대한) 경쟁이 많은 시나리오에서는 좋은 성능을 보여준듯합니다.

  2. 저 EPIC 공지에서 중요한건 “64비트 & 에디터”

    언리얼에서 직접 만들어서 쓰는 메모리 관리자가 32비트만 지원했습니다. 근데 “에디터”에서 메모리를 미친듯이 쓰다보니 일단 64비트로 포팅을 했고, 메모리 관리자는 다시 만들어야 하는 수준이라 기본 malloc, free를 쓰고 있었지요. TBB를 걍 붙여준다니 에픽에서 우왕ㅋ굳ㅋ 했을 것 같네요.

    • 언리얼 에디터가 (지나가면서 뒤에서 본거지만) 무진장 뽀대나던데 메모리도 무진장(…)쓰나요 -_-;;;;

      intel TBB를 붙여주는건 사실 라이센스 살펴본걸로는 그냥 붙여서 배포해도 상관은 없어 보이던데(….). 뭐 이걸로 양쪽의 마케팅 효과라거나.

      • 게임에서 쓰는 리소스가 커질 수록 메모리도 많이 쓰게 되지요. GoW1의 경우는 2GB에서 충분했지만 GoW2에서는 2GB가 간당간당했었던듯. 32비트 컴파일할 때 옵션 줘서 레벨디자이너들은 64비트 머신에서 3GB로 작업했던거 같아요.

        • GoW2 art asset이 어떻길래 -_;;;;;;
          3GB <- 이건 유저 영역 3GB 주고 쓴단 얘기죠? 근데 이건 뻘소리지만 32bit에서도 NTLDR 에 옵션 잘 줘서 3G로 쓰는게 있지 않나요(잘 모르겠지만;;; )

    • 인텔이 게임 업계를 자주 들여다보는 듯하긴해요. TBB 쪽 포럼글 봐도 그런 얘기가 꽤 있고 -_-;
      Scientific Computation 말고 TBB/concurrent programing 제일 많이 하는 타겟을 여기로 잡는듯함;

  3. 윈도우 7에서 LFH을 켠 상태로 테스트로 사용하신 코드를 이용해 둘 다 비교를 해봤는 tbb쪽이 훨씬 빠르더군요.
    LFH은 평균 24초가 나왔고 TBB는 평균 10초를 기록했습니다. 코어수가 4개라서 스레드를 4개 만들고 테스트를 해봤습니다.

    • 제가 수행한 테스트도 Windows Vista에서니 LFH에서 차이가 2배 정도였을테지만, 역시 코어 수가 늘어나면 메모리 경쟁이 더 심해지니 tbb allocator가 성능이 더 좋아지긴하네요;

  4. TBB 적용해서 테스팅을 해보니 역시 훨씬 빠르네요. 하긴 빠르지 않다면 따로 메모리 할당자를 만들어서 배포할 이유도 없긴 하겠지요. 흠.

    다만 저렇게 모든 경우에 대해 메모리 경쟁이 일어나는 케이스 말고 평균보다 좀 심한 정도에서는 얼마나 차이가 날지 궁금하네요. 이런 건 모델링하기가 어려우니 실제 어플에 적용을 해봐야 알 것 같긴 한데…

    • 저거보단 무조건 차이가 적겠죠(…). 사실 실제 응용에선 메모리 할당량이 뻔하면 미리 할당해놓고 시작하는 경우도 많고요.
      모델링하기 어려운…편인건 맞는데 실제로 저걸 프로파일링해서 테스트하는 방법이 없는건 아니긴 합니다. 다만 완전히 실제랑 같게는 잘 안될 뿐이지만;;;

Leave a Reply