zoukankan      html  css  js  c++  java
  • TCMalloc小记

    
    周末抽空看了一下tcmalloc,了解了个大概。下面记录一下。

    一. 原理

    tcmalloc就是一个内存分配器,管理堆内存,主要影响malloc和free,用于降低频繁分配、释放内存造成的性能损耗,并且有效地控制内存碎片。glibc中的内存分配器是ptmalloc2,tcmalloc号称要比它快。一次malloc和free操作,ptmalloc需要300ns,而tcmalloc只要50ns。同时tcmalloc也优化了小对象的存储,需要更少的空间。tcmalloc特别对多线程做了优化,对于小对象的分配基本上是不存在锁竞争,而大对象使用了细粒度、高效的自旋锁(spinlock)。分配给线程的本地缓存,在长时间空闲的情况下会被回收,供其他线程使用,这样提高了在多线程情况下的内存利用率,不会浪费内存,而这一点ptmalloc2是做不到的。

    tcmalloc区别的对待大、小对象。它为每个线程分配了一个线程局部的cache,线程需要的小对象都是在其cache中分配的,由于是thread local的,所以基本上是无锁操作(在cache不够,需要增加内存时,会加锁)。同时,tcmalloc维护了进程级别的cache,所有的大对象都在这个cache中分配,由于多个线程的大对象的分配都从这个cache进行,所以必须加锁访问。在实际的程序中,小对象分配的频率要远远高于大对象,通过这种方式(小对象无锁分配,大对象加锁分配)可以提升整体性能。

    线程级别cache和进程级别cache实际上就是一个多级的空闲块列表(Free List)。一个Free List以大小为k bytes倍数的空闲块进行分配,包含n个链表,每个链表存放大小为nk bytes的空闲块。在tcmalloc中,<=32KB的对象被称作是小对象,>32KB的是大对象。在小对象中,<=1024bytes的对象以8n bytes分配,1025<size<=32KB的对象以128n bytes大小分配,比如:要分配20bytes则返回的空闲块大小是24bytes的,这样在<=1024的情况下最多浪费7bytes,>1025则浪费127bytes。而大对象是以页大小4KB进行对齐的,最多会浪费4KB - 1 bytes。下图就是一个基本的free list的示意图:


    实际上,一个free list(我称之为空闲块列表)就是一个数组索引多个链表,每个链表存放相同大小的块。可以根据要分配的内存大小size算出合适的块在free list中的下标,然后找到对应的空闲块链表。

    tcmalloc的数据结构组织如下:



    Thread-local free list:线程本地的空闲块cache,用于分配小对象。

    Heap free list:中心free list,全局唯一,用于按页对齐分配大对象或者是将连续的多个页(被称作span)分割成多个小对象的空闲块分配给thread-local free list。

    Page array:用于描述当前tcmalloc持有的内存状态,完成的是从page number到span的映射。

    下面看一下小对象的分配:

    (1)根据分配的size计算出对应的空闲块大小,从而确定对应空闲块链表,然后从thread local的free list进行分配。

    (2)如果的空闲块链表非空,直接将头结点对应的空闲块返回并从空闲块链表中将其删除。

    (3)如果空闲块链表是空的,需要从heap free list获取一个span。如果heap free list非空,则将span切分成多个相同大小的空闲块插入空闲块链表中,然后返回头结点。

    (4)如果heap free list是空的,则调用sbrk或者mmap进行内存的分配一系列连续的内存页,作为span,然后切分成多个相同大小的空闲块插入空闲块链表,然后返回头结点。

    大对象的分配就要简单多了,直接从heap free list分配4nKB大小的空闲块即可,如果heap free list不存在该大小的空闲块,通过系统调用分配连续的内存页。

    tcmalloc还会对thread local cache进行垃圾收集,从而避免内存浪费。

    二. 安装和使用

    tcmalloc属于gperftools,安装比较简单,需要注意一下,在64bit系统上需要先安装libunwind(http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz,只能是这个版本),这个库为基于64位CPU和操作系统的程序提供了基本的堆栈辗转开解功能,其中包括用于输出堆栈跟踪的API、用于以编程方式辗转开解堆栈的API以及支持C++异常处理机制的API,32bit系统不需安装。在安装过程中,可能出现下列错误:

    1. gcc -DHAVE_CONFIG_H -I. -I../include -I../include -I../include/tdep-x86_64 -I. -D_GNU_SOURCE -DNDEBUG -g -O2 -fexceptions -Wall -Wsign-compare -MT setjmp/longjmp.lo -MD -MP -MF setjmp/.deps/longjmp.Tpo -c setjmp/longjmp.c -fPIC -DPIC -o setjmp/.libs/longjmp.o  
    2. /usr/include/x86_64-linux-gnu/bits/setjmp2.h:26:13: error: 'longjmp' aliased to undefined symbol '_longjmp  
    gcc -DHAVE_CONFIG_H -I. -I../include -I../include -I../include/tdep-x86_64 -I. -D_GNU_SOURCE -DNDEBUG -g -O2 -fexceptions -Wall -Wsign-compare -MT setjmp/longjmp.lo -MD -MP -MF setjmp/.deps/longjmp.Tpo -c setjmp/longjmp.c -fPIC -DPIC -o setjmp/.libs/longjmp.o
    /usr/include/x86_64-linux-gnu/bits/setjmp2.h:26:13: error: 'longjmp' aliased to undefined symbol '_longjmp
    是因为缺少编译选项U_FORTIFY_SOURCE,解决方法有两种:

    1,尚未调用configure进行配置,则执行CPPFLAGS=-U_FORTIFY_SOURCE ./configure ... 会自动将编译选项添加到Makefile中。

    2,已经配置过,直接修改Makefile,查找CPPFLAGS然后添加上-U_FORTIFY_SOURCE。

    然后就是安装gperftools,这个正常安装即可,默认安装到/usr/local/lib下,完成后调用lddconfig添加到动态链接库缓存,然后就可以使用了。

    使用方法,很简单,在编译时加上tcmalloc动态链接库即可

    1. g++ test.cpp -ltcmalloc  
    g++ test.cpp -ltcmalloc

    源码不需任何修改,tcmalloc会自动替换掉glibc默认的malloc和free,简简单单的一条命令就可以提升不少性能,very good。

  • 相关阅读:
    CNN comprehension
    Gradient Descent
    Various Optimization Algorithms For Training Neural Network
    gerrit workflow
    jenkins job配置脚本化
    Jenkins pipeline jobs隐式传参
    make words counter for image with the help of paddlehub model
    make words counter for image with the help of paddlehub model
    git push and gerrit code review
    image similarity
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318250.html
Copyright © 2011-2022 走看看