C++性能优化 —— TCMalloc的原理与使用

news2024/10/25 18:24:23

一、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_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"

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;
    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秒。

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

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

相关文章

进制之间的转换

文章目录 编译过程进制转换1、进制的概念1.1 二进制1.2 八进制1.3 十六进制 进制在程序中的表现方式十进制转二进制将十进制转换成二进制&#xff08;除2反序取余法&#xff09;二进制转十进制&#xff08;权值法&#xff09; 八进制转十进制将十进制转换成八进制(除8反序取余法…

JAVA方法概述

一.方法的定义 public class MethodDemo1 {public static void main(String[] args) {// 目标&#xff1a;掌握定义方法的完整格式&#xff0c;搞清楚使用方法的好处// 需求&#xff1a;假如现在很多程序员都要进行2个整数求和的操作// 王程序员int rs sum(10,20);System.out.…

哪款洗地机值得买?希亦、追觅、米博、美的谁才是行业标杆?

在家庭清洁中&#xff0c;最让我们苦恼的便是厨房垃圾了&#xff0c;油渍跟食物残渣&#xff0c;用扫把扫了后&#xff0c;要反反复复的湿拖五六次&#xff0c;期间不停的手洗拖把&#xff0c;这套流程下来&#xff0c;往往容易腰酸背痛&#xff0c;手指皱巴巴的&#xff0c;这…

Docker 配置阿里云镜像加速器

一、首先需要创建一个阿里云账号 二、登录阿里云账号 三、进入控制台 四、搜索容器镜像服务&#xff0c;并选择 五、选择镜像工具中的镜像加速 六 、配置镜像源 注意&#xff1a;有/etc/docker文件夹的直接从第二个命令开始

Stable Diffusion 提示词语法(Prompt)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本篇文章主要讲述 Stable Diffusion 提示词语法&#xff0c;主要包括&#xff1a;提示词的概念、提示词的长度、权重、分步绘制、交替绘制、组合绘制等&#x…

品鉴民俗 巧手绘梦--2024年海淀区元宵节主题文化活动圆满举办

为深入挖掘传统节日的文化内涵和历史意义,引导人民群众弘扬中华优秀文化和传统美德,让广大群众过一个热热闹闹、红红火火、充满文化气息的元宵佳节,2024年2月24日上午,由北京市海淀区文化和旅游局主办、海淀区文化馆承办的“品鉴民俗 巧手绘梦”——2024年海淀区元宵节主题文化…

● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II

● 121. 买卖股票的最佳时机 因为只能买卖一次&#xff0c;所以左边找一个最小的&#xff0c;右边找一个最大的&#xff0c;相减的差就是最大的利润。那么用贪心来做&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int nprices.size()…

在 echarts 的 rich 中使用 iconfont 图标库图标作为 backgroundColor.image 值的方法

实现步骤 1、引入 iconfont.js。该脚本执行时&#xff0c;会在 body 下插入一个 svg 标签&#xff0c;标签下包含了图标库中的 svg 图标 path。 <script src"your/iconfont/path/iconfont.js"></script>或者 import your/iconfont/path/iconfont.js2、…

如何禁止员工在上班时间利用电脑打游戏逛娱乐网站?

在现代化的工作环境中&#xff0c;电脑已成为员工日常工作的必需品。然而&#xff0c;一些员工可能在上班时间利用电脑进行非工作相关的活动&#xff0c;如打游戏或浏览娱乐网站。这不仅影响个人的工作效率&#xff0c;也可能对团队的整体绩效和公司的文化产生负面影响。因此&a…

饮料换购 刷题笔记

直接开个计数器mask 每当饮料现存数-1&#xff1b; cnt;且mask; 一旦mask达到3 饮料现存数 计数器清零3 代码 #include <iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int main(){ int n; …

火爆全网,软件测试数据库常用 SQL 语句总结,你要的我都有......

前言 直接上干货 数据定义语言(DDL) 主要负责数据库、数据表、视图、键、索引等结构化的操作 常用的语句有&#xff1a;CREATE DATABASE、CREATE TABLE、ALTER TABLE等 字段的常用约束有&#xff1a;PRIMARY KEY、FOREIGN KEY、NOT NULL、UNIQUE、AUTO_INCREMENT、DEFAULT 常…

使用nginx输入端口号显示404

输入对应的端口号显示404 先检查当前nginx文件夹的路径是没有中文的查看是否没有开启nginx&#xff1a;ctrlaltdelete打开任务管理器&#xff0c;看看有没有nginx.exe进程&#xff08;一般是有两个进程&#xff09;如果没有进程说明没有打开nginx&#xff0c;查看端口号是否被…

菜鸟笔记-14散点图标记形状

大家在学习Python科研绘图中&#xff0c;总会涉及散点图标记形状&#xff0c;为了方便大家学习应用&#xff0c;博主通过学习搜集&#xff0c;将这部分技巧总结如下。 14.1默认散点图 14.1.1图像呈现 14.1.2绘图代码 import numpy as np # 导入numpy库&#xff0c;用于处理…

创建RAID0,RAID5并管理,热备盘,模拟故障

目录 1. RAID介绍以及mdadm安装 1.1 安装mdadm工具 2. 创建raid0 2.1 环境准备 2.2 使用两个磁盘创建RAID0 2.3 查看RAID0信息 2.4 对创建的RAID0进行格式化并挂载 2.5 设置成开机挂载 2.6 删除RAID0 3. 创建raid5 3.1 环境准备 3.2 用3个磁盘来模拟R…

Spring boot2.7整合jetcache 远程redis缓存方案

前文 java Spring boot简述jetcache 并叙述后续文章安排 我们讲述了 jetcache 的基本概念 那么 本文 我们现在开始 直接开工 首先 要保证 redis启动 而且 要将其中全部的键值对清空掉 pom.xml中 加入坐标 <!-- https://mvnrepository.com/artifact/com.alicp.jetcache/je…

C++初阶 类(上)

目录 1. 什么是类 2. 如何定义出一个类 3. 类的访问限定符 4. 类的作用域 5. 类的实例化 6. 类的大小 7. this指针 1.this指针的引出 2. this指针的特性 8. 面试题 1. 什么是类 在C语言中&#xff0c;不同类型的数据集合体是结构体。为了方便管理结构体&#xff0c;我…

HCIP---IS-IS协议

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.IS-IS协议概述 IS-IS是一种基于链路状态的内部网关协议&#xff08;IGP&#xff09;&#xff0c;它使用最短路径优先算法&#xff08;SPF或Dijkstra&#xff09;进行路由计算。这种协议在自治…

MySQl基础入门⑤

上一遍知识内容 1.数据表的结构及数据完整性的设计 1、关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关…

精准获客、优化体验,Xinstall数据自动分析全搞定

在移动互联网时代&#xff0c;App已经成为了我们生活中不可或缺的一部分。然而&#xff0c;对于App开发者来说&#xff0c;如何有效地评估渠道效果、精准获客以及优化用户体验&#xff0c;一直是一个令人头疼的问题。幸运的是&#xff0c;Xinstall作为一款一站式App全渠道统计服…

组合逻辑电路(一)(加法器)

目录 组合逻辑电路的特点及功能描述 采用SSI的组合逻辑电路的分析与设计 采用SSI的组合逻辑电路的分析 采用SII的组合逻辑电路的设计 例 例 例 常用的MSI组合逻辑电路 半加器 全加器 加法器 串行进位加法器 例 例 超前进位加法器 组合逻辑电路的特点及功能描述 小规模集成…