开源项目|高性能内存分配库mimalloc

793 阅读5分钟

1、简介

mimalloc 是微软研究院在2019年发表并开源的一个新的内存分配库(github.com/microsoft/m…
尝试可以先clone到本地:

git clone https://github.com/microsoft/mimalloc.git

第二步可以先编译:

cmake ../mimalloc/ && make && make install 

第三步可以将头文件拷贝到系统目录,如:

cp -r ./mimalloc/include/* /usr/include/

最后编译代码的时候带上 mimalloc

gcc -o test ./test.c -lmimalloc

如果不编译直接在程序中使用可以尝试,如下:

LD_PRELOAD=/usr/lib/libmimalloc.so myprogram

环境变量选项:

  • MIMALLOC_SHOW_STATS=1: 程序运行时统计内存使用信息
  • MIMALLOC_VERBOSE=1: 显示debug日志
  • MIMALLOC_SHOW_ERRORS=1: 显示error和warning信息
  • MIMALLOC_PURGE_DELAY=N: 对于不再使用的内存,延时Nms在回收(默认10)
  • MIMALLOC_ARENA_EAGER_COMMIT=1: 对于申请大内存时(1GiB),打开立即向OS申请内存开关
  • MIMALLOC_USE_NUMA_NODES=N: 设置最多有N个NUMA节点,如果未设置,那么在实际运行时检测NUMA节点

2、对比其他的内存库

(1)代码量少,核心代码行数 < 3500 行

  • tcmalloc ~20k LOC
  • jemalloc ~25k LOC

(2)性能大大优于市面上其他 memory allocator

  • 比 tcmalloc 快 7%
  • 比 jemalloc 快 14%

图片

图片

(其中mi:mimalloc,tc:tcmalloc,je:jemalloc ...)

(3)多线程设计

  • mimalloc是专门为多线程应用设计的一款高性能的内存分配器,能极大降低多线程环境下线程间同步带来的性能下降,与tcmalloc类似,mimalloc采用了线程本地缓存的设计,同时采用了复杂的缓存管理机制提升内存分配和释放的速度;
  • mimalloc的内存分配/释放算法是lock-free的,意味着它是async-signal-safe;

3、架构

图片

架构

在mimalloc中,每个线程都有一个Thread Local的堆,每个线程在进行内存的分配时均从该线程对应的堆上进行分配,在一个堆中会有一个或多个segment,一个segment会对应一个或多个page,而内存的分配就是在这些page上进行。

  • tlb:大多数高性能内存分配器产品都会实现自己的内存池(缓存),以减少系统调用(mmap)的次数,mimalloc 将缓存进一步划分到各个线程中,各线程的内存分配都走线程本地缓存(tld),最大程度避免了内存分配时线程间的(锁)竞争;
  • heap:一级缓存,是分配内存时的第一搜索位置,其内部使用了相当巧妙的结构和算法来组织和管理内存块,以实现内存块的快速索引和改善连续内存分配时的局域性;
  • segment:面向 heap 的二级缓存,是通过mmap申请的连续大内存区域(32MB),起到了缓冲heap频繁调整大小的作用;
  • page:page 是 mimalloc 中内存管理的最小单位,它由大小固定block序列和元数据组成,这些block由隶属于page的 free list 组织,当进行内存申请时,最终返回的就是 free list 中第一个block;
  • free list:上层应用请求分配内存时block的直接来源,大部分时间直接 pop-from-head 即可,只有在变空时才从另外两条 free list 补充block;
  • local-free list:上层应用(同一线程)释放内存时block的直接归处,大部分时间直接 push-to-head 即可;
  • thread-free list :上层应用(跨线程)释放内存时block的直接归处,通过 lock-free 算法实现 push-to-head 即可;

其中对 free list 操作的代码:

...
page->free = page->local_free; // move the list
page->local_free = NULL; // and the local list is empty again

...
void atomic_push( block_t** list, block_t* block ) {
 do { block->next = *list; }
 while (!atomic_compare_and_swap(list, block, block->next));
}

...
tfree = atomic_swap( &page->thread_free, NULL );
append( page->free, tfree );

4、内存申请和释放源码

(1)malloc实现

extern inline voidmi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
    void* p;
    if (mi_likely(size <= MI_SMALL_SIZE_MAX)) {
        p = mi_heap_malloc_small(heap, size);
    }
    else {
        p = _mi_malloc_generic(heap, size);
    }
    return p;
}

voidmalloc_smallsize_t n ) // 0 < n <= 1024
    heap_t* heap = tlb;           //线程局部存储指针指向的线程局部堆
    page_t* page = heap->pages_direct[(n+7)>>3]; // divide up by 8
    block_t* block = page->free;  //空闲列表
    if (block==NULLreturn malloc_generic(heap,n); // slow path
    page->free = block->next;     //移动空闲列表指针
    page->used++;
    return block;
}

//slow path,mimalloc机制保证此方法会隔一段时间被调到
voidmalloc_genericheap_t* heap, size_t size ) {
    deferred_free();        // 调用用户自定义的方法
    // 遍历现有page
    foreach( page in heap->pages[size_class(size)] ) {
        page_collect(page); //回收页内空间
        if (page->used - page->thread_freed == 0) {
          page_free(page);
        }
        else if (page->free != NULL) {
          return malloc(size);
        }
    }
    .... // 现有页中无空闲空间,重新分配一页,并从新页中分配空间
}

void page_collect(page) {
    page->free = page->local_free; // 将空闲列表设置为本地空闲列表
    page->local_free = NULL;       // 本地空闲列表置空
    ... // 原子地处理 线程释放 列表
}

(2)free实现

void free( void* p ) {
  //找到p所在的segment
  segment_t* segment = (segment_t*)((uintptr_t)p & ~(4*MB));
  if (segment==NULL) return;
  //找到p所在的page
  page_t* page = &segment->pages[(p - segment) >> segment->page_shift];
  block_t* block = (block_t*)p;

  if (thread_id() == segment->thread_id) { // free的是本线程分配的内存(local free)
    block->next = page->local_free;
    page->local_free = block;
    page->used--;
    if (page->used - page->thread_freed == 0) page_free(page);
  }
  else { // free的是其他线程分配的内存(non-local free)
    atomic_push( &page->thread_free, block);
    atomic_incr( &page->thread_freed );
  }
}

参考

[1] www.cnblogs.com/linkwk7/p/1…
[2] github.com/microsoft/m…
[3] www.cnblogs.com/linkwk7/p/1…