C++性能優化(九) —— TCMalloc

C++性能優化(九) —— TCMalloc

一、TCMalloc簡介

1、TCMalloc簡介

TCMalloc(Thread-Caching Malloc,線程緩存的malloc)是Google開發的內存分配算法庫,最初作爲Google性能工具庫 perftools 的一部分,提供高效的多線程內存管理實現,用於替代操作系統的內存分配相關的函數(malloc、free,new,new[]等),具有減少內存碎片、適用於多核、更好的並行性支持等特性。
TCMalloc屬於gperftools,gperftools項目包括heap-checker、heap-profiler、cpu-profiler、TCMalloc等組件。
gperftools源碼地址:
https://github.com/gperftools/gperftools
TCMalloc源碼地址:
https://github.com/google/tcmalloc




2、TCMalloc安裝

(1)TCMalloc源碼安裝
bazel源增加:
/etc/yum.repos.d/bazel.repo

[copr:copr.fedorainfracloud.org:vbatts:bazel]
name=Copr repo for bazel owned by vbatts
baseurl=https://download.copr.fedorainfracloud.org/results/vbatts/bazel/epel-7-$basearch/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://download.copr.fedorainfracloud.org/results/vbatts/bazel/pubkey.gpg
repo_gpgcheck=0
enabled=1
enabled_metadata=1

在線安裝bazel:
yum install bazel3
TCMalloc源碼下載:

git clone https://github.com/google/tcmalloc.git
cd tcmalloc && bazel test //tcmalloc/...

由於TCMalloc依賴gcc 9.2+,clang 9.0+: -std=c++17,因此推薦使用其它方式安裝。

(2)gperftools源碼安裝

gperftools源碼下載:
git clone https://github.com/gperftools/gperftools.git
生成構建工具:
autogen.sh
配置編譯選項:
configure --disable-debugalloc --enable-minimal
編譯:make -j4
安裝:make install
TCMalloc庫安裝在/usr/local/lib目錄下。
(3)在線安裝
epel源安裝:
yum install -y epel-release
gperftools安裝:
yum install -y gperftools.x86_64












3、Linux64位系統支持

在Linux64位系統環境下,gperftools使用glibc內置的stack-unwinder可能會引發死鎖,因此官方推薦在配置和安裝gperftools前,先安裝libunwind-0.99-beta。
在Linux64位系統上使用libunwind只能使用TCMalloc,但heap-checker、heap-profiler和cpu-profiler不能正常使用。
如果不希望安裝libunwind,也可以用gperftools內置的stack unwinder,但需要應用程序、TCMalloc庫、系統庫(比如libc)在編譯時開啓幀指針(frame pointer)選項。
在x86-64下,編譯時開啓幀指針選項並不是默認行爲。因此需要指定-fno-omit-frame-pointer編譯所有應用程序,然後在configure時通過--enable-frame-pointers選項使用內置的gperftools stack unwinder。


二、TCMalloc架構

1、TCMalloc架構

C++性能優化(九) —— TCMalloc
Front-end(前端):負責提供快速分配和重分配內存給應用,由Per-thread cache和Per-CPU cache兩部分組成。
Middle-end(中臺):負責給Front-end提供緩存。當Front-end緩存內存不夠用時,從Middle-end申請內存。
Back-end(後端):負責從操作系統獲取內存,並給Middle-end提供緩存使用。
TCMalloc中每個線程都有獨立的線程緩存ThreadCache,線程的內存分配請求會向ThreadCache申請,ThreadCache內存不夠用會向CentralCache申請,CentralCache內存不夠用時會向PageHeap申請,PageHeap不夠用就會向OS操作系統申請。
TCMalloc將整個虛擬內存空間劃分爲n個同等大小的Page,將n個連續的page連接在一起組成一個Span;PageHeap向OS申請內存,申請的span可能只有一個page,也可能有n個page。
(1)Page
Page是操作系統對內存管理的單位,TCMalloc中以Page爲單位管理內存,Page默認大小爲8KB,通常爲Linux系統中Page大小的倍數關係,如8、32、64,可以在編譯選項配置時通過--with-tcmalloc-pagesize參數指定。
Page越大,TCMalloc的速度相對越快,但其佔用的內存也會越高。默認Page大小通過減少內存碎片來最小化內存使用,使用更大的Page則會帶來更多的內存碎片,但速度上會有所提升。
(2)Span
Span是PageHeap中管理內存Page的單位,由一個或多個連續的Page組成,比如2個Page組成的span,多個span使用鏈表來管理,TCMalloc以Span爲單位向操作系統申請內存。
C++性能優化(九) —— TCMalloc
第1個span包含2個page,第2個和第4個span包含3個page,第3個span包含5個page。
Span會記錄起始page的PageID(start)以及所包含page的數量(length)。
Span要麼被拆分成多個相同size class的小對象用於小對象分配,要麼作爲一個整體用於中對象或大對象分配。當作用作小對象分配時,span的sizeclass成員變量記錄了其對應的size class。
span中包含兩個Span類型的指針(prev,next),用於將多個span以鏈表的形式存儲。
Span有三種狀態:IN_USE、ON_NORMAL_FREELIST、ON_RETURNED_FREELIST。
IN_USE是正在使用中,要麼被拆分成小對象分配給CentralCache或者ThreadCache,要麼已經分配給應用程序。
ON_NORMAL_FREELIST是空閒狀態。
ON_RETURNED_FREELIST指span對應的內存已經被PageHeap釋放給系統。
(3)ThreadCache
ThreadCache是每個線程獨立擁有的Cache,包含多個空閒內存鏈表(size classes),每一個鏈表(size-class)都有大小相同的object。
線程可以從各自Thread Cache的FreeList獲取對象,不需要加鎖,所以速度很快。如果ThreadCache的FreeList爲空,需要從CentralCache中的CentralFreeList中獲取若干個object到ThreadCache對應的size class列表中,然後再取出其中一個object返回。
(4)Size Class
TCMalloc定義了很多個size class,每個size class都維護了一個可分配的FreeList,FreeList中的每一項稱爲一個object,同一個size-class的FreeList中每個object大小相同。
在申請小內存時(小於256K),TCMalloc會根據申請內存大小映射到某個size-class中。比如,申請0到8個字節的大小時,會被映射到size-class1中,分配8個字節大小;申請9到16字節大小時,會被映射到size-class2中,分配16個字節大小,以此類推。
C++性能優化(九) —— TCMalloc
(5)CentralCache
CentralCache是ThreadCache的緩存,ThreadCache內存不足時會向CentralCache申請。CentralCache本質是一組CentralFreeList,鏈表數量和ThreadCache數量相同。ThreadCache中內存過多時,可以放回CentralCache中。
如果CentralFreeList中的object不夠,CentralFreeList會向PageHeap申請一連串由Span組成的Page,並將申請的Page切割成一系列的object後,再將部分object轉移給ThreadCache。
當申請的內存大於256K時,不在通過ThreadCache分配,而是通過PageHeap直接分配大內存。
C++性能優化(九) —— TCMalloc
(6)PageHeap
PageHeap保存存儲Span的若干鏈表,CentralCache內存不足時,可以從PageHeap獲取Span,然後把Span切割成object。
PageHeap申請內存時按照Page申請,但管理內存的基本單位是Span,Span代表若干連續Page。
C++性能優化(九) —— TCMalloc
C++性能優化(九) —— TCMalloc
PageHeap組織結構如下:
C++性能優化(九) —— TCMalloc





































2、Front-end

Front-end處理對特定大小內存的請求,有一個內存緩存用於分配或保存空閒內存。Front-end緩存一次只能由單個線程訪問,不需要任何鎖,因此大多數分配和釋放都很快。
只要有適當大小的緩存內存,Front-end將滿足任何請求。如果特定大小的緩存爲空,Front-end將從Middle-end請求一批內存來填充緩存。Middle-end包括CentralfReelList和TransferCache。
如果Middle-end內存耗盡,或者用戶請求的內存大小大於Front-end緩存的最大值,則請求將轉到Back-end,以滿足大塊內存分配,或重新填充Middle-end的緩存。Back-end也稱爲PageHeap。
Front-end由兩種不同的實現模式:
(1)Per-thread
TCMalloc最初支持對象的Per-thread緩存,但會導致內存佔用隨着線程數增加而增加。現代應用程序可能有大量的線程,會導致每個線程佔用內存累積起來很大,也可能會導致由單個較小線程緩存累積起來的內存佔用會很大。
(2)Per-CPU
TCMalloc近期開始支持Per-CPU模式。在Per-CPU模式下,系統中的每個邏輯CPU都有自己的緩存,可以從中分配內存。在x86架構,邏輯CPU相當於一個超線程。






3、Middle-end

Middle-end負責向Front-end提供內存並將內存返回Back-end。Middle-end由Transfer cache和Central free list組成,每個類大小都有一個Transfer cache和一個Central free list。緩存由互斥鎖保護,因此訪問緩存會產生串行化成本。
(1)Transfer cache
當Front-end請求內存或返回內存時,將訪問Transfer cache。
Transfer cache保存一個指向空閒內存的指針數組,可以快速地將對象移動到數組中,或者代表Front-end從數組中獲取對象。
當一個線程正在分配另一個線程釋放的內存時,Transfer cache就可以得到內存名稱。Transfer cache允許內存在兩個不同的線程之間快速流動。
如果Transfer cache無法滿足內存請求,或者沒有足夠的空間容納返回的對象,Transfer cache將訪問Central free list。
(2)Central Free List
Central Free List使用spans管理內存,span是一個或多個TCMalloc內存Page的集合。
一個或多個對象的內存請求由Central Free List來滿足,方法是從span中提取對象,直到滿足請求爲止。如果span中沒有足夠的可用對象,則會從Back-end請求更多的span。
當對象返回到Central Free List時,每個對象都映射到其所屬的span(使用pagemap,然後釋放到span中)。如果駐留在指定span中的所有對象都返回給span,則整個span將返回給Back-end。








4、Back-end

TCMalloc中Back-end有三項職責:
(1)管理大量未使用的內存塊。
(2)負責在沒有合適大小的內存來滿足分配請求時從操作系統獲取內存。
(3)負責將不需要的內存返回給操作系統。
TCMalloc有兩種Back-end:
(1)Legacy Pageheap,管理TCMalloc中Page大小的內存塊。
Legacy Pageheap是一個可用內存連續頁面的特定長度的空閒列表數組。對於k<256,kth條目是由k個TCMalloc頁組成的運行的免費列表。第256項是長度大於等於256頁的運行的免費列表
C++性能優化(九) —— TCMalloc
(2)支持hugepage的pageheap,以hugepage大小的內存塊來管理內存。管理hugepage內存塊中內存,使分配器能夠通過減少TLB未命中率來提高應用程序性能。







三、TCMalloc內存分配原理

1、TCMalloc內存分配簡介

C++性能優化(九) —— TCMalloc
TCMalloc按照所分配內存的大小將內存分配分爲三類:小對象分配(0, 256KB]、中對象分配(256KB, 1MB]、大對象分配(1MB, +∞)。
TCMalloc分配小對象分配時,在應用程序和內存之間其實有三層緩存:PageHeap、CentralCache、ThreadCache;TCMalloc分配中對象和大對象時,只有PageHeap緩存。

2、小內存分配

(1)Size Class
對於小於256KB的小對象,TCMalloc按大小劃分85個類別(Size Class),每個size class都對應一個大小,比如8字節,16字節,32字節。應用程序申請內存時,TCMalloc會首先將所申請的內存大小向上取整到size class的大小,比如1~8字節之間的內存申請都會分配8字節,9~16字節之間都會分配16字節,以此類推。
(2)ThreadCache
TCMalloc爲每個線程保存獨立的線程緩存,稱爲ThreadCache。ThreadCache中對於每個size class都有一個獨立的FreeList,緩存n個未被應用程序使用的空閒對象。
TCMalloc對於小對象的分配直接從ThreadCache的FreeList中返回一個空閒對象,小對象的回收也將其重新放回ThreadCache中對應的FreeList中。由於每個線程的ThreadCache是獨立的,因此從ThreadCache中取用或回收內存是不需要加鎖的,速度很快。
爲了方便統計數據,各線程的ThreadCache連接成一個雙向鏈表。ThreadCache結構如下:
C++性能優化(九) —— TCMalloc
(3)CentralCache
ThreadCache中的空閒對象來自所有線程的公用緩存CentralCache。CentralCache中對於每個size class也都有一個單獨的鏈表來緩存空閒對象,稱爲CentralFreeList,供各線程的ThreadCache從中獲取空閒對象。線程從CentralCache中取用或回收對象,是需要加鎖的。爲了平攤鎖操作的開銷,ThreadCache一般從CentralCache中一次性取用或回收多個空閒對象。
CentralCache在TCMalloc中是一個邏輯上的概念,本質是CentralFreeList類型的數組。CentralCache簡化結構如下:
C++性能優化(九) —— TCMalloc
(4)PageHeap
當CentralCache中的空閒對象不夠用時,CentralCache會向PageHeap申請一塊內存(可能來自PageHeap的緩存,也可能向系統申請新的內存),並將其拆分成一系列空閒對象,添加到對應size class的CentralFreeList中。
PageHeap內部根據內存塊(span)的大小採取了兩種不同的緩存策略。128個page以內的span,每個大小都用一個鏈表來緩存,超過128個page的span,存儲於一個有序set(std::set)。
C++性能優化(九) —— TCMalloc
(5)內存回收
應用程序調用free或delete一個小對象時,僅是將其插入到ThreadCache中其size class對應的FreeList中,不需要加鎖,因此速度非常快。
只有當滿足一定的條件時,ThreadCache中的空閒對象纔會重新放回CentralCache中,以供其它線程取用。當滿足一定條件時,CentralCache中的空閒對象也會還給PageHeap,PageHeap再還給系統。
















3、中對象分配

TCMalloc對於超過256KB但不超過1MB(128個page)的中對象分配採取了與小對象不同的分配策略。TCMalloc會將應用程序所要申請的內存大小向上取整到整數個page(會產生1B~8KB內部碎片),然後向PageHeap申請一個指定page數量的span並返回其起始地址即可。
對於128個page以內的span,PageHeap中有128個span的鏈表,分別對應1~128個page的span。
C++性能優化(九) —— TCMalloc
假設要分配一塊內存,其大小經過向上取整後對應k個page,因此需要從PageHeap取一個大小爲k個page的span。分配過程如下:
(1)首先從k個page的span鏈表開始,到128個page的span鏈表,按順序找到第一個非空鏈表。
(2)取出非空鏈表中的一個span,假設有n個page,將span拆分成兩個span:一個span大小爲k個page,作爲分配結果返回;另一個span大小爲n – k個page,重新插入到n – k個page的span鏈表中。
(3)如果找不到非空鏈表,則將分配看做是大對象分配。
4、大內存分配
TCMalloc對於超過1MB(128個page)的大對象分配需要先將所要分配的內存大小向上取整到整數個page,假設是k個page,然後向PageHeap申請一個k個page大小的span。
大對象分配用到的span都是超過128個page的span,其緩存方式不是鏈表,而是一個按span大小排序的有序set(std::set),以便按大小進行搜索。
假設要分配一塊超過1MB的內存,其大小經過向上取整後對應k個page(k>128),或者是要分配一塊1MB以內的內存,但無法由中對象分配邏輯來滿足,此時k <= 128。分配過程如下:
(1)搜索span set,找到不小於k個page的最小的span,假設span有n個page。
(2)將span拆分爲兩個span:一個span大小爲k個page,作爲結果返回;另一個span大小爲n – k個page,如果n – k > 128,則將其插入到大span的set中,否則,將其插入到對應的小span鏈表中。
(3)如果找不到合適的span,則使用sbrk或mmap向系統申請新的內存以生成新的span,並重新執行中對象或大對象的分配算法。












四、TCMalloc使用指南

1、TCMalloc庫簡介

libtcmalloc_and_profiler.so
libtcmalloc_debug.so
libtcmalloc_minimal_debug.so
libtcmalloc_minimal.so
libtcmalloc.so
libtcmalloc_minimal版本不包含heap profiler和heap checker。




2、動態庫方式

通過-ltcmalloc或-ltcmalloc_minimal將TCMalloc鏈接到應用程序。
通過LD_PRELOAD預載入TCMalloc庫可以不用重新編譯應用程序即可使用TCMalloc。
LD_PRELOAD="/usr/lib/libtcmalloc.so"

3、靜態庫方式

在編譯選項的最後加入/usr/local/lib/libtcmalloc_minimal.a鏈接靜態庫。

4、TCMalloc生效

TCMalloc在libc_override.h中實現了覆蓋機制,在使用指定-ltcmalloc鏈接後,應用程序對malloc、free、new、delete等調用就從默認glibc中的函數調用變爲TCMalloc庫中相應的函數調用。
(1)僅使用GLibc庫
在glibc中,內存分配相關的函數都是弱符號(weak symbol),因此TCMalloc只需要定義自己的函數將其覆蓋即可。
libc_override_redefine.h中定義如下:


void* operator new(size_t size) { return TCMallocInternalNew(size); }
void operator delete(void* p) noexcept { TCMallocInternalDelete(p); }
void* operator new[](size_t size) { return TCMallocInternalNewArray(size); }
void operator delete[](void* p) noexcept { TCMallocInternalDeleteArray(p); }
void* operator new(size_t size, const std::nothrow_t& nt) noexcept {
  return TCMallocInternalNewNothrow(size, nt);
}
void* operator new[](size_t size, const std::nothrow_t& nt) noexcept {
  return TCMallocInternalNewArrayNothrow(size, nt);
}
void operator delete(void* ptr, const std::nothrow_t& nt) noexcept {
  return TCMallocInternalDeleteNothrow(ptr, nt);
}
void operator delete[](void* ptr, const std::nothrow_t& nt) noexcept {
  return TCMallocInternalDeleteArrayNothrow(ptr, nt);
}
extern "C" {
    void* malloc(size_t s) noexcept { return TCMallocInternalMalloc(s); }
    void free(void* p) noexcept { TCMallocInternalFree(p); }
    void sdallocx(void* p, size_t s, int flags) {
      TCMallocInternalSdallocx(p, s, flags);
    }
    void* realloc(void* p, size_t s) noexcept {
      return TCMallocInternalRealloc(p, s);
    }
    void* calloc(size_t n, size_t s) noexcept {
      return TCMallocInternalCalloc(n, s);
    }
    void cfree(void* p) noexcept { TCMallocInternalCfree(p); }
    void* memalign(size_t a, size_t s) noexcept {
      return TCMallocInternalMemalign(a, s);
    }
    void* valloc(size_t s) noexcept { return TCMallocInternalValloc(s); }
    void* pvalloc(size_t s) noexcept { return TCMallocInternalPvalloc(s); }
    int posix_memalign(void** r, size_t a, size_t s) noexcept {
      return TCMallocInternalPosixMemalign(r, a, s);
    }
    void malloc_stats(void) noexcept { TCMallocInternalMallocStats(); }
    int mallopt(int cmd, int v) noexcept { return TCMallocInternalMallOpt(cmd, v); }
    #ifdef HAVE_STRUCT_MALLINFO
    struct mallinfo mallinfo(void) noexcept {
      return TCMallocInternalMallocInfo();
    }
    #endif
    size_t malloc_size(void* p) noexcept { return TCMallocInternalMallocSize(p); }
    size_t malloc_usable_size(void* p) noexcept {
      return TCMallocInternalMallocSize(p);
    }
}  // extern "C"

(2)使用GCC編譯器編譯
如果使用了GCC編譯器,則使用其支持的函數屬性:alias。
libc_override_gcc_and_weak.h:

#define TCMALLOC_ALIAS(tc_fn) \
  __attribute__((alias(#tc_fn), visibility("default")))

void* operator new(size_t size) noexcept(false)
    TCMALLOC_ALIAS(TCMallocInternalNew);
void operator delete(void* p) noexcept TCMALLOC_ALIAS(TCMallocInternalDelete);
void operator delete(void* p, size_t size) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteSized);
void* operator new[](size_t size) noexcept(false)
    TCMALLOC_ALIAS(TCMallocInternalNewArray);
void operator delete[](void* p) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArray);
void operator delete[](void* p, size_t size) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArraySized);
void* operator new(size_t size, const std::nothrow_t& nt) noexcept
    TCMALLOC_ALIAS(TCMallocInternalNewNothrow);
void* operator new[](size_t size, const std::nothrow_t& nt) noexcept
    TCMALLOC_ALIAS(TCMallocInternalNewArrayNothrow);
void operator delete(void* p, const std::nothrow_t& nt) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteNothrow);
void operator delete[](void* p, const std::nothrow_t& nt) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArrayNothrow);

void* operator new(size_t size, std::align_val_t alignment) noexcept(false)
    TCMALLOC_ALIAS(TCMallocInternalNewAligned);
void* operator new(size_t size, std::align_val_t alignment,
                   const std::nothrow_t&) noexcept
    TCMALLOC_ALIAS(TCMallocInternalNewAligned_nothrow);
void operator delete(void* p, std::align_val_t alignment) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteAligned);
void operator delete(void* p, std::align_val_t alignment,
                     const std::nothrow_t&) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteAligned_nothrow);
void operator delete(void* p, size_t size, std::align_val_t alignment) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteSizedAligned);
void* operator new[](size_t size, std::align_val_t alignment) noexcept(false)
    TCMALLOC_ALIAS(TCMallocInternalNewArrayAligned);
void* operator new[](size_t size, std::align_val_t alignment,
                     const std::nothrow_t&) noexcept
    TCMALLOC_ALIAS(TCMallocInternalNewArrayAligned_nothrow);
void operator delete[](void* p, std::align_val_t alignment) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArrayAligned);
void operator delete[](void* p, std::align_val_t alignment,
                       const std::nothrow_t&) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArrayAligned_nothrow);
void operator delete[](void* p, size_t size,
                       std::align_val_t alignemnt) noexcept
    TCMALLOC_ALIAS(TCMallocInternalDeleteArraySizedAligned);

extern "C" {
void* malloc(size_t size) noexcept TCMALLOC_ALIAS(TCMallocInternalMalloc);
void free(void* ptr) noexcept TCMALLOC_ALIAS(TCMallocInternalFree);
void sdallocx(void* ptr, size_t size, int flags) noexcept
    TCMALLOC_ALIAS(TCMallocInternalSdallocx);
void* realloc(void* ptr, size_t size) noexcept
    TCMALLOC_ALIAS(TCMallocInternalRealloc);
void* calloc(size_t n, size_t size) noexcept
    TCMALLOC_ALIAS(TCMallocInternalCalloc);
void cfree(void* ptr) noexcept TCMALLOC_ALIAS(TCMallocInternalCfree);
void* memalign(size_t align, size_t s) noexcept
    TCMALLOC_ALIAS(TCMallocInternalMemalign);
void* aligned_alloc(size_t align, size_t s) noexcept
    TCMALLOC_ALIAS(TCMallocInternalAlignedAlloc);
void* valloc(size_t size) noexcept TCMALLOC_ALIAS(TCMallocInternalValloc);
void* pvalloc(size_t size) noexcept TCMALLOC_ALIAS(TCMallocInternalPvalloc);
int posix_memalign(void** r, size_t a, size_t s) noexcept
    TCMALLOC_ALIAS(TCMallocInternalPosixMemalign);
void malloc_stats(void) noexcept TCMALLOC_ALIAS(TCMallocInternalMallocStats);
int mallopt(int cmd, int value) noexcept
    TCMALLOC_ALIAS(TCMallocInternalMallOpt);
struct mallinfo mallinfo(void) noexcept
    TCMALLOC_ALIAS(TCMallocInternalMallocInfo);
size_t malloc_size(void* p) noexcept TCMALLOC_ALIAS(TCMallocInternalMallocSize);
size_t malloc_usable_size(void* p) noexcept
    TCMALLOC_ALIAS(TCMallocInternalMallocSize);
}  // extern "C"

將宏展開,__attribute__ ((alias ("tc_malloc"), default))表明tc_malloc是malloc的別名。

5、TCMalloc調優

默認情況下,TCMaloc會將長時間未用的內存交還系統。tcmalloc_release_rate標識用於控制交回頻率,可以在運行時強制調用ReleaseFreeMemory回收未使用內存。
MallocExtension::instance()-&gt;ReleaseFreeMemory();
通過 SetMemoryReleaseRate來設置tcmalloc_release_rate。如果設置爲0,代表永遠不交回;數字越大代表交回的頻率越大,值在0-10之間,可以通過設置 TCMALLOC_RELEASE_RATE環境變量來設置。
GetMemoryReleaseRate可以查看當前釋放的概率值。
TCMALLOC_SAMPLE_PARAMETER:採樣時間間隔,默認值爲0。
TCMALLOC_RELEASE_RATE:釋放未使用內存的頻率,默認值爲1.0。
TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLD:內存最大分配閾值,默認值爲1073741824(1GB)。
TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES:分配給線程緩衝的最大內存上限,默認值爲16777216(16MB)。






6、TCMalloc測試

malloc.cpp:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#define MAX_OBJECT_NUMBER       (1024)
#define MAX_MEMORY_SIZE         (1024*100)

struct BufferUnit{
   int   size;
   char* data;
};

struct BufferUnit   buffer_units[MAX_OBJECT_NUMBER];

void MallocBuffer(int buffer_size) {

for(int i=0; i<MAX_OBJECT_NUMBER; ++i)  {
    if (NULL != buffer_units[i].data)   continue;

    buffer_units[i].data = (char*)malloc(buffer_size);
    if (NULL == buffer_units[i].data)  continue;

    memset(buffer_units[i].data, 0x01, buffer_size);
    buffer_units[i].size = buffer_size;
    }
}

void FreeHalfBuffer(bool left_half_flag) {
    int half_index = MAX_OBJECT_NUMBER / 2;
    int min_index = 0;
    int max_index = MAX_OBJECT_NUMBER-1;
    if  (left_half_flag)
        max_index =  half_index;
    else
        min_index = half_index;

    for(int i=min_index; i<=max_index; ++i) {
        if (NULL == buffer_units[i].data) continue;

        free(buffer_units[i].data);
        buffer_units[i].data =  NULL;
        buffer_units[i].size = 0;
    }
}

int main() {
    memset(&buffer_units, 0x00, sizeof(buffer_units));
    int decrease_buffer_size = MAX_MEMORY_SIZE;
    bool left_half_flag   =   false;
    time_t  start_time = time(0);
    while(1)  {
        MallocBuffer(decrease_buffer_size);
        FreeHalfBuffer(left_half_flag);
        left_half_flag = !left_half_flag;
        --decrease_buffer_size;
        if (0 == decrease_buffer_size) break;
    }
    FreeHalfBuffer(left_half_flag);
    time_t end_time = time(0);
    long elapsed_time = difftime(end_time, start_time);

    printf("Used %ld seconds. \n", elapsed_time);
    return 1;
}

使用TCMalloc編譯鏈接:
g++ malloc.cpp -o test -ltcmalloc
執行test,耗時334秒。
使用默認GLibc編譯鏈接:
g++ malloc.cpp -o test
執行test,耗時744秒。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章