본문 바로가기

Dev/C++

멀티쓰레드 최적화 힙 메모리 할당기 - tcmalloc. jemalloc

# TCmalloc (Thread-Caching memory allocation)

https://code.google.com/p/gperftools/

- 구글이 만든 성능 도구에 포함되어 있는 힙 메모리 할당자로서 크롬 및 많은 프로젝트에 사용 

- 기존의 malloc 으로 대표되는 할당자의 경우 멀티쓰레드의 최적화가 고려되지 않은 상황의 구현체이기 때문에, Thread가 난무하는 현실 세계에서는 많은 성능 저하가 있다고 함 (메모리 할당, 관리도 동시성을 고려해야 하므로..)

- 이쪽 계열 라이브러리 중에서는 가장 많이 알려져 있고, 기본 할당기에 비해 비약적인 성능 향상이 있다고 함.

- 그리고 적용 방법도 드라마틱 하게, 인상적임. 



# 리눅스(Ubuntu, 14.10) 환경에서의 TCmalloc 

- 패키지로 설치 하던가, 소스를 받아서 설치 하면 됨  

sudo apt-get install libtcmalloc-minimal4


* 사용하는 방법 3가지 

1. tc_malloc 및 tc_free 등의 함수를 사용하여 malloc 이나 C++의 기본 할당자를 직접 대체하는 방법


2. 라이브러리를 링크하여 C 및 C++ 기본 할당자는 대체하는 방법

TCmalloc는 라이브러리를 링크하는 것 만으로, C 및 C++의 기본 할당자를 tmalloc의 할당자로 대체 해줌.(Patch)

g++ -o mybin mybin.cpp -ltcmalloc_minimal 


3. LD_PRELOAD를 이용하여 모듈을 먼저 로드(후킹)시킨 후, 기본 할당기를 대체하는 방식 

허나 구글에서는 이 방법에 대해서는 tricky 한 방법이므로, 2번 방법을 사용 하도록 권고함

LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so mybin

http://gperftools.googlecode.com/git/doc/tcmalloc.html

 

* 성능 

- 극단적인 할당과 해제를 반복하는 멀티쓰레드 환경에서, 기본 할당자 보다 3배이상 빠른 결과를 보임

- 물론 일반적인 로직의 경우 메모리 할당, 해제에 관련해서 비중이 얼마나 많이 차이나느냐에 따라 다르겠지만, 메모리 할당에서의 성능 향상 뿐만 아니라, 지속적이 서비스로 인한 메모리 단편화를 감소시켜 바꾸는 것만으로 20% 정도의 성능 향상을 보인 다고 함.

  


# 윈도우 환경에서의 TCmalloc 

- 소스 받아서 MSVC로 설치 하면 됨. 친절하게 MSVC 프로젝트 파일로 제공 됨  

- 솔루션에 많은 프로젝트들이 보이는데, libtcmalloc_minimal 라고 되어 있는 프로젝트만 Release 로 빌드하면, 동적링크 모듈(DLL)로 빌드되고 정적으로 바꾸고 빌드해서 사용해도 무방한 것으로..


* 사용하는 방법 2가지 

1. 위의 리눅스의 예와 같음. TCmalloc의 커스텀 할당자로 구현 하면 됨


2. 위도우 환경에서는 Patch 방식이 되지 않으므로, 라이브러리만 링크하는 것으로 끝나지는 않고 강제로 링크 심볼을 정의 함으로서 기본 할당자가 교체 되도록 설계 함 (이것도 멋짐!!)

// 소스에 아래와 같이 추가하여 라이브러리 참조 및 링크 심볼을 주입 함 

#pragma comment (lib, "libtcmalloc_minimal")

#pragma comment (linker, "/include:__tcmalloc") 


// 프로젝트 속성 환경에서 추가 할때는,  

// 구성속성-링커-입력-추가종속성 에 명시하여 링크를 추가하고, 

// 밑에 "강제 기호 참조"에 __tcmalloc 를 넣어주면 됨


* 성능 

- 극단적인 환경에서는 30% 이상 효과가 있고, 일반적인 업무 로직에서는 그보다는 작겠지만 어느정도 성능 향상 효과가 있을 것으로 판단.

- 기본적으로 MSVC 환경에서의 메모리 할당자가 리눅스쪽의 것보다는 좋아 보임.


--


# JEMalloc 

- Jason Evans가 만들었고, 페이스북이나 파이어폭스에서 사용 한다고 함

- 성능은 TCmalloc 보다 약간 좋다고는 (주장)하나, 개인적으로 테스트 할때는 TCmalloc 보다 약간 부족 했음.

- 구글링의 결과로는 보다 다이나믹한 멀티쓰레드 환경에서 최적화 되었다는데 반복적인 할당과 해제를 반복하는 예제에서는 그다지 많은 차이를 보이지 않음.


# 리눅스 환경에서의 JEMalloc

http://www.canonware.com/jemalloc/

- TCmalloc 과 같이 소스 및 패키지 설치 지원 

sudo apt-get install libjemalloc-dev


* 사용하는 방법 

- 위의 TCmalloc과 같이 JEMalloc의 커스컴 할당기(je_malloc, je_free) 를 사용 하던가 LD_PRELOAD를 이용하여 사용하는 방법이 있음 

- JEMalloc도 링크 만으로 기본 할당자를 교체 해주나, C언어의 기본 할당자 malloc 계열만 교체 해주고 C++의 것은 교체 해주지 않음. 그래서 C++에서 사용하려면(STL 등에서) 사용자 정의 할당자를 만들고 그 곳에 JEMalloc으로 할당 하는 방식으로 처리해야 함

- 리눅스의 경우 C, C++ 할당자를 모두 교체 지원 함


- 윈도우의 경우도 링크 참조만으로 기본 할당기를 교체해주지는 않고, header 파일을 이용해서 매크로로 C언어 기본 할당자를 교체 해주는 방식으로 처리 (#include <jemalloc/jemalloc.h>)

- Mingw (gcc 4.9.2)에서는 컴파일이 잘되나, MSVC(2013) 에서는 컴파일이 몇가지 문제를 일으킴 

Github에 Win32용 포팅 버전이 있으므로 그것을 받아서 빌드 하면 됨  


# 기타

- nedmalloc : 윈도우즈 환경에 더 최적화 되었다는 nedmalloc 이런것도 있음. nedmalloc의 경우 자체 C언어 계열 커스텀 할당 함수와 C++의 allocator 를 대체할 클래스도 제공.


- tbbmalloc (Threading Building Blocks) : 인텔에서 만든 쓰레딩관련 라이브러리에 포함 되어 있는 힙 메모리 할당기 

- 사이트에서 다운 받으면 OS별 바이너리가 들어 있음.

// linux 

sudo apt-get install libtbb-dev

g++ -o mybin mybin.cpp -ltbbmalloc_proxy


// MSVC, tcmalloc 과 같은 방식

#pragma comment (lib, "tbbmalloc_proxy")

#pragma comment (linker, "/include:___TBB_malloc_proxy") 



Detours Express 3.0Detours is a library for intercepting arbitrary Win32 binary functions on x86 machines.

// 설치 후, 설치 디렉토리에서 nmake 로 빌드 후 사용 

// dll 프리로드 후킹 (Windows)

withdll.exe /d:libtcmalloc_minimal.dll mybin




# 추가, 윈도우 테스트 결과 (참고용) 

소스 : https://gist.github.com/cdecl/e7b414e8eff4a67c9396


* vc++ 기본 (msvc 2013)

[std::string alloc]

1585, 1656, 1600, 1707, 1628,

[malloc()]

1451, 1411, 1381, 1489, 1399,


* tcmalloc 

[std::string alloc]

1156, 1150, 1195, 1045, 1184,

[malloc()]

560, 586, 564, 571, 564,


* tbbmalloc (Threading Building Blocks)

[std::string alloc]

1003, 973, 963, 998, 1000,

[malloc()]

811, 782, 780, 813, 807,



# 추가, 리눅스 테스트 결과 (참고용) 

- 소스 : malloc 반복횟수 및 옵티마이저 안되게 끔 위의 소스 수정 


* 기본 (g++ 4.9.1)

[std::string alloc]

3931, 3904, 3945, 3902, 4044,

[malloc()]

1434, 1422, 1447, 1457, 1411,


* tcmalloc

[std::string alloc]

1112, 1125, 1161, 1099, 1091,

[malloc()]

355, 333, 328, 351, 334,


* jemalloc

[std::string alloc]

1451, 1442, 1396, 1427, 1411,

[malloc()]

438, 483, 432, 462, 457,


* tbbmalloc

[std::string alloc]

1856, 1843, 1820, 1868, 1842,

[malloc()]

750, 701, 765, 764, 752,



참고 : 

one malloc to rule them all

http://blog.reverberate.org/2009/02/one-malloc-to-rule-them-all.html


Scalable memory allocation using jemalloc

https://www.facebook.com/notes/facebook-engineering/scalable-memory-allocation-using-jemalloc/480222803919


Benchmarks of the Lockless Memory Allocator

http://locklessinc.com/benchmarks_allocator.shtml


C++ memory allocation mechanism performance comparison (tcmalloc vs. jemalloc)

http://stackoverflow.com/questions/7852731/c-memory-allocation-mechanism-performance-comparison-tcmalloc-vs-jemalloc