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源码地址:

GitHub - 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架构

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操作系统申请。

相关视频推荐

linux c/c++后端开发中的重点技术:内存管理(内存管理架构、numa、slab、vmalloc、内存池、内存泄漏、物理内存、虚拟内存、MMU机制icon-default.png?t=N7T8https://www.bilibili.com/video/BV1Vc411F7wv/

Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

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为单位向操作系统申请内存。

第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个字节大小,以此类推。

(5)CentralCache

CentralCache是ThreadCache的缓存,ThreadCache内存不足时会向CentralCache申请。CentralCache本质是一组CentralFreeList,链表数量和ThreadCache数量相同。ThreadCache中内存过多时,可以放回CentralCache中。

如果CentralFreeList中的object不够,CentralFreeList会向PageHeap申请一连串由Span组成的Page,并将申请的Page切割成一系列的object后,再将部分object转移给ThreadCache。

当申请的内存大于256K时,不在通过ThreadCache分配,而是通过PageHeap直接分配大内存。

(6)PageHeap

PageHeap保存存储Span的若干链表,CentralCache内存不足时,可以从PageHeap获取Span,然后把Span切割成object。

PageHeap申请内存时按照Page申请,但管理内存的基本单位是Span,Span代表若干连续Page。

PageHeap组织结构如下:

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页的运行的免费列表

(2)支持hugepage的pageheap,以hugepage大小的内存块来管理内存。管理hugepage内存块中内存,使分配器能够通过减少TLB未命中率来提高应用程序性能。

三、TCMalloc内存分配原理

1、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个未被应用程序使用的空闲对象。

ThreadCache是一个TSL对象,小于256K的小内存申请均由ThreadCache进行分配;通过ThreadCache分配过程中不需要任何锁,可以极大的提高分配速度。

TCMalloc对于小对象的分配直接从ThreadCache的FreeList中返回一个空闲对象,小对象的回收也将其重新放回ThreadCache中对应的FreeList中。由于每个线程的ThreadCache是独立的,因此从ThreadCache中取用或回收内存是不需要加锁的,速度很快。

为了方便统计数据,各线程的ThreadCache连接成一个双向链表。ThreadCache结构如下:

(3)CentralCache

ThreadCache中的空闲对象来自所有线程的公用缓存CentralCache。CentralCache中对于每个size class也都有一个单独的链表来缓存空闲对象,称为CentralFreeList,供各线程的ThreadCache从中获取空闲对象。线程从CentralCache中取用或回收对象,是需要加锁的。为了平摊锁操作的开销,ThreadCache一般从CentralCache中一次性取用或回收多个空闲对象。

CentralCache在TCMalloc中是一个逻辑上的概念,本质是CentralFreeList类型的数组。CentralCache简化结构如下:

(4)PageHeap

当CentralCache中的空闲对象不够用时,CentralCache会向PageHeap申请一块内存(可能来自PageHeap的缓存,也可能向系统申请新的内存),并将其拆分成一系列空闲对象,添加到对应size class的CentralFreeList中。

PageHeap被所有线程共享(分配时需要全局锁定),负责与操作系统的直接交互(申请及释放内存),并且大尺寸的内存申请直接通过PageHeap进行分配。

PageHeap内部根据内存块(span)的大小采取了两种不同的缓存策略。128个page以内的span,每个大小都用一个链表来缓存,超过128个page的span,存储于一个有序set(std::set)。

(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。

假设要分配一块内存,其大小经过向上取整后对应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:Debug版本。

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_MALLINFOstruct mallinfo mallinfo(void) noexcept {return TCMallocInternalMallocInfo();}#endifsize_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) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteSized);void* operator new[](size_t size) noexcept(false)TCMALLOC_ALIAS(TCMallocInternalNewArray);void operator delete[](void* p) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteArray);void operator delete[](void* p, size_t size) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteArraySized);void* operator new(size_t size, const std::nothrow_t& nt) noexceptTCMALLOC_ALIAS(TCMallocInternalNewNothrow);void* operator new[](size_t size, const std::nothrow_t& nt) noexceptTCMALLOC_ALIAS(TCMallocInternalNewArrayNothrow);void operator delete(void* p, const std::nothrow_t& nt) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteNothrow);void operator delete[](void* p, const std::nothrow_t& nt) noexceptTCMALLOC_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&) noexceptTCMALLOC_ALIAS(TCMallocInternalNewAligned_nothrow);void operator delete(void* p, std::align_val_t alignment) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteAligned);void operator delete(void* p, std::align_val_t alignment,const std::nothrow_t&) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteAligned_nothrow);void operator delete(void* p, size_t size, std::align_val_t alignment) noexceptTCMALLOC_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&) noexceptTCMALLOC_ALIAS(TCMallocInternalNewArrayAligned_nothrow);void operator delete[](void* p, std::align_val_t alignment) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteArrayAligned);void operator delete[](void* p, std::align_val_t alignment,const std::nothrow_t&) noexceptTCMALLOC_ALIAS(TCMallocInternalDeleteArrayAligned_nothrow);void operator delete[](void* p, size_t size,std::align_val_t alignemnt) noexceptTCMALLOC_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) noexceptTCMALLOC_ALIAS(TCMallocInternalSdallocx);void* realloc(void* ptr, size_t size) noexceptTCMALLOC_ALIAS(TCMallocInternalRealloc);void* calloc(size_t n, size_t size) noexceptTCMALLOC_ALIAS(TCMallocInternalCalloc);void cfree(void* ptr) noexcept TCMALLOC_ALIAS(TCMallocInternalCfree);void* memalign(size_t align, size_t s) noexceptTCMALLOC_ALIAS(TCMallocInternalMemalign);void* aligned_alloc(size_t align, size_t s) noexceptTCMALLOC_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) noexceptTCMALLOC_ALIAS(TCMallocInternalPosixMemalign);void malloc_stats(void) noexcept TCMALLOC_ALIAS(TCMallocInternalMallocStats);int mallopt(int cmd, int value) noexceptTCMALLOC_ALIAS(TCMallocInternalMallOpt);struct mallinfo mallinfo(void) noexceptTCMALLOC_ALIAS(TCMallocInternalMallocInfo);size_t malloc_size(void* p) noexcept TCMALLOC_ALIAS(TCMallocInternalMallocSize);size_t malloc_usable_size(void* p) noexceptTCMALLOC_ALIAS(TCMallocInternalMallocSize);}  // extern "C"

5、TCMalloc调优

默认情况下,TCMaloc会将长时间未用的内存交还系统。tcmalloc_release_rate标识用于控制交回频率,可以在运行时强制调用ReleaseFreeMemory回收未使用内存。

MallocExtension::instance()->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;elsemin_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秒。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/727229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于AFDPF主动频率偏移法的孤岛检测Simulink仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于AFDPF主动频率偏移法的孤岛检测Simulink仿真。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB2022a 36 4.系统原理简介 在分布式发电系统中&#xff0c;孤…

express基础

express express介绍 官网传送门基于 Node.js 平台&#xff0c;快速、开放、极简的 Web 开发框架express特点 Web 应用 Express 是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架&#xff0c;它提供一系列强大的特性&#xff0c;帮助你创建各种 Web 和移动设备应用。…

详讲Spring的面向切片编程(AOP)二

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

3月7日代码随想录组合及优化

77.组合 77. 组合 - 力扣&#xff08;LeetCode&#xff09; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3…

【手游联运平台搭建】游戏平台的作用

随着科技的不断发展&#xff0c;游戏行业也在不断壮大&#xff0c;而游戏平台作为连接玩家与游戏的桥梁&#xff0c;发挥着越来越重要的作用。游戏平台不仅为玩家提供了便捷的游戏体验&#xff0c;还为游戏开发者提供了广阔的市场和推广渠道。本文将从多个方面探讨游戏平台的作…

el-table 表格多选, 批量删除功能

一、基础的多选el-table ElementUI 提供了多选行table&#xff0c;同时若依框架也提供了成熟的多选表格。 1.table基础结构 需要绑定selection-change方法 <el-tablev-loading"loading"stripe:data"productList"selection-change"handleSelect…

node的安装与介绍

安装 下载地址 node官网首页就会有两个安装选择&#xff0c;会根据当前电脑的系统自动显示对应的安装包&#xff0c;一个长期维护版&#xff08;LTS&#xff09;,一个是尝鲜版&#xff0c;记住选择LTS版本 安装指定版本下载截图 安装过程截图&#xff08;非常简单&#xff…

计算机设计大赛 深度学习花卉识别 - python 机器视觉 opencv

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &a…

C++ 路径问题

目录 例1 例2 例3 例4 例5 例6 例1 62. 不同路径 1.初始化 2.当前位置的条数&#xff0c;就是上面位置的条数 &#xff0c;加上其左边位置的条数&#xff0c;dp[i][j] dp[i - 1][j] dp[i][j - 1]; 参考代码 class Solution { public:int uniquePaths(int m, int n) …

个推与华为深度合作,成为首批支持兼容HarmonyOS NEXT的服务商

自华为官方宣布HarmonyOS NEXT鸿蒙星河版开放申请以来&#xff0c;越来越多的头部APP宣布启动鸿蒙原生开发&#xff0c;鸿蒙生态也随之进入全新发展的第二阶段。 作为华为鸿蒙生态的重要合作伙伴&#xff0c;个推一直积极参与鸿蒙生态建设。为帮助用户在HarmonyOS NEXT上持续享…

力扣刷题Days11第二题--141. 环形链表(js)

目录 1,题目 2&#xff0c;代码 2.1快慢指针 2.2&#xff0c;哈希表 3&#xff0c;学习与总结 3.1自己尝试写快慢指针 反思 1,题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&…

PostgreSQL安装教程

系统环境 下载压缩包 下载压缩包 解压压缩包 查看解压文件 编译安装 编译 安装 用户权限和环境变量设置 创建用户 创建数据目录和日志目录 设置权限 设置环境变量 初始化数据库 数据库访问控制配置文件 postgresql.conf pg_hba.conf PostgreSQL启动与关闭 手…

Docker:部署微服务集群

1. 部署微服务集群 实现思路&#xff1a; ① 查看课前资料提供的cloud-demo文件夹&#xff0c;里面已经编写好了docker-compose文件 ② 修改自己的cloud-demo项目&#xff0c;将数据库、nacos地址都命名为docker-compose中的服务名 ③ 使用maven打包工具&#xff0c;将项目…

Pytorch入门实战 P1-实现手写数字识别

目录 一、前期准备&#xff08;环境数据&#xff09; 1、首先查看我们电脑的配置&#xff1b; 2、使用datasets导入MNIST数据集 3、使用dataloader加载数据集 4、数据可视化 二、构建简单的CNN网络 三、训练模型 1、设置超参数 2、编写训练函数 3、编写测试函数 4、…

子事务的应用

子事务的应用 1. 为什么要使用子事务&#xff1f; 为了防止接口执行失败时&#xff0c;导致事务回滚&#xff0c;接口日志记录不到日志表里面&#xff0c;因而将记录日志表的方法写成子事务的方法。 2. 怎么使用子事务&#xff1f; 在方法名后面加上“_RequiresNew”&#xff…

文物藏品信息管理系统的优势

本系统支持一普标准所有管理信息&#xff0c;包括保管信息、基本情况、鉴定信息、考古发掘信息、来源信息、流传经历、损坏记录、移动记录、修复记录、展览信息、著录信息、收藏单位信息等的管理和维护。 能够实现对藏品信息进行动态管理&#xff0c;提供藏品信息管理指标的维护…

《人工智能怎么学》荣获2023年吴文俊人工智能科学技术奖及赠书活动

中国人工智能学会官网&#xff08;www.caai.cn&#xff09;近日正式公布了2023年吴文俊科学技术奖获奖名单&#xff0c;图书《人工智能怎么学》项目被授予2023年吴文俊人工智能科学技术奖科技进步奖&#xff08;科普项目&#xff09;。2023年吴文俊科学技术奖完整获奖名单见htt…

YOLOv8官方仓库更新,添加YOLOv9模型

目录 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; 摘要 PGI&GELAN 代码实现 实验结果 消融实验 可视化 结论 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查…

开发充电桩APP提高管理效能

随着社会的发展&#xff0c;电动车已经成为城市交通的重要组成部分&#xff0c;用户所下载的充电类的APP也非常大&#xff0c;而充电桩的建设和利用效率成为了一个亟待解决的问题。在这个背景下&#xff0c;物联网技术的应用成为了提高充电桩效能的关键。虎克技术公司在此领域提…

CyberChef加密解密RSA、AES中文乱码问题有效解决办法

一、AES加密 AES的ECB模式加密&#xff0c;秘钥&#xff1a;1234567812345678 加密效果与utf-8本地加密一致 二、AES解密 AES的ECB模式解密&#xff0c;秘钥&#xff1a;1234567812345678 同理RSA加密设置一样