当前位置: 首页 > 工具软件 > mimalloc > 使用案例 >

mimalloc简单的使用

韩欣怿
2023-12-01

最近实现一个多线程内存池,所以看了很多开源的代码,其中微软的mimalloc无论从性能和代码量上有表现得很好,其实验数据表明相比于jemalloc、tcmalloc等实现大约快了10%,所以这里对其使用做个简单的介绍,具体原理可以研读论文和参阅其他专家的博客。

mimalloc的开源库地址:https://github.com/microsoft/mimalloc

虚拟内存是和进程相关的,进程中的所有线程共享同一个虚拟内存空间,因此一个线程想要请求一块内存,必须考虑数据竞争的情况,即防止同时有另一个线程也在请求这一块内存。一个保守的做法是在请求内存时加锁,这也是很多旧的内存管理器的做法,但加锁太慢了,无法发挥多核的优势。

现代的内存分配器都尽量不用加锁的方式,取而代之的是将内存和线程关联起来,每个线程管理着自己的内存池,分配时只从线程自己的内存池分配,这样就避免了锁的使用;释放时分两种情况,一种是本线程的释放,这和分配一样不需要加锁;另一种是内存块被转移到另一个线程去,并由那个线程执行释放,这种情况就需要一些线程同步的处理,等一会我们会介绍 mimalloc 怎么做。

mimalloc 和其他分配器类似,每个线程都有一块线程本地数据(tld),专门用来管理线程相关的内存。tld主要管理着两个东西:

segment:segment 有点类似于其他分配器的 slab 或 arena 的概念,它是从OS申请出来的比较大的内存块,所有程序分配的内存块都自出segment;tld就维护着segment的列表。

heap:是对内存堆的抽象,也可认为它是和线程相关的内存池,它管理着当前线程所有可分配的内存页(page);

page是比segment更细粒度的单位,实际上page就是从segment切分而来的,一个page是一个固定尺寸的内存块(block)集合。

相关原理可以查阅论文:https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action/
这里来简单介绍下使用方式:

一.作为库使用
mimalloc使用cmake作为构建工具, 执行以下命令即可编译.
成功编译后在构建目录下会生成所需(libmimalloc)的动态库/静态库以及测试工具.

mkdir -p [dir to build]
cd [dir to build]
cmake [source path]
make

如果是使用编译生成的库,可以在源文件中包含mimalloc.h并增加-lmimalloc选项来使用。
可以通过在源文件中包含mimalloc.h并增加-lmimalloc选项来使用mimalloc.
mimalloc可以与其它allocator共存, 对于cmake构建可以使用以下命令设置:
target_link_libraries([program] PUBLIC mimalloc)
如果不希望重新编译也可以通过设置环境变量来替换:
env LD_PRELOAD=[path/to/libmimalloc.so] [program]

如果需要打印libmalloc的调试信息, 可以设置环境变量(注意后者需要debug版本):
env MIMALLOC_VERBOSE=1 [program]
env MIMALLOC_SHOW_STATS=1 [program]

其它选项:
打印错误/告警信息: MIMALLOC_SHOW_ERRORS=1
使用huge page特性: MIMALLOC_LARGE_OS_PAGES=1

如下是一个示例:

**1.先来看下源码**
[23:18:20] hansy@hansy:~$ cat 1.c
#include <sys/time.h>
int main() {
  struct timeval last;
  struct timeval next;
  gettimeofday(&last, 0);
  for (int i = 0; i < 10000000; ++i) {
    int *p = malloc(i * sizeof(int));
    free(p);
  }
  gettimeofday(&next, 0);
  printf("%llu.%06llu\n",
    (next.tv_usec > last.tv_usec ? next.tv_sec - last.tv_sec : next.tv_sec - 1 - last.tv_sec),
    (next.tv_usec > last.tv_usec ? next.tv_usec - last.tv_usec : 1000000 + next.tv_usec - last.tv_usec));
  return 0;
}

**2.运行源码**
[23:18:25] hansy@hansy:~$ gcc 1.c -w && ./a.out 
8.606776

**3.设置环境变量进行调试**
[23:18:42] hansy@hansy:~$ env LD_PRELOAD=./mimalloc/build_release/libmimalloc.so ./a.out 
1.314598

**4.打印调试信息**
[23:18:57] hansy@hansy:~$ env MIMALLOC_VERBOSE=1 LD_PRELOAD=./mimalloc/build_release/libmimalloc.so ./a.out 
mimalloc: process init: 0x7f4b0d558740
mimalloc: option 'large_os_pages': 0
mimalloc: option 'secure': 0
mimalloc: option 'page_reset': 0
mimalloc: option 'cache_reset': 0
1.323777
mimalloc: option 'show_stats': 0
heap stats:     peak      total      freed       unit      count  
   elapsed:     1.324 s
   process: user: 1.299 s, system: 0.012 s, faults: 0, reclaims: 479, rss: 2.6 mb
mimalloc: process done: 0x7f4b0d558740

二.作为源代码直接编译使用
如果仅仅使用源码可以编译static.c后进行链接,在源码中包含mimalloc.h
如下是一个简单Makefile文件

CC=gcc
CFLAGS=-I ../include
DEPS = mimalloc.h
SRCS = main.c
%.o: %.c $(DEPS)
	$(CC) -c -o $@ $< $(CFLAGS)

all:  static.o 
	$(CC) $(SRCS) $(CFLAGS) -o main -lpthread  

.PHONY: clean

clean:
	rm -f ./*.o
	rm -f main

关于API的使用这里做个简单的介绍,mimalloc原生API

mi_malloc()
mi_free()
mi_calloc()
mi_realloc()
......
其他API可以参与mimalloc-doc.h文件

这些API是可以替代malloc使用的,已经实现了线程安全。
三.这里对mi_stats_print的打印信息做一个简单的介绍

heap stats:    peak      total      freed    current       unit      count   
//下面是分配的次数和每次分配的大小,最后显示ok则表示已经进行释放
normal  25:    1.2 KiB    1.2 KiB    1.2 KiB      0        1.2 KiB      1      ok
normal  28:    2.0 KiB    2.0 KiB    2.0 KiB      0        2.0 KiB      1      ok
normal  33:    5.0 KiB    5.0 KiB    5.0 KiB      0        5.0 KiB      1      ok
//下面是对上述分配自元进行汇总
heap stats:    peak      total      freed    current       unit      count   
    normal:    8.2 Ki     8.2 Ki     8.2 Ki       0        2.7 KiB      3      ok
     large:      0          0          0          0                            ok
      huge:      0          0          0          0                            ok
     total:    8.2 KiB    8.2 KiB    8.2 KiB      0                            ok
malloc req:    6.9 KiB    6.9 KiB    6.9 KiB      0                            ok

  reserved:  128.0 MiB  128.0 MiB      0      128.0 MiB                        not all freed!
 committed:   66.0 MiB   66.1 MiB  128.5 KiB   66.0 MiB                        not all freed!
     reset:      0          0          0          0                            ok
   touched:  269.8 KiB  269.8 KiB  132.5 KiB  137.2 KiB                        not all freed!
  segments:      3          3          2          1                            not all freed!
-abandoned:      1          1          1          0                            ok
   -cached:      0          0          0          0                            ok
     pages:      3          3          1          2                            not all freed!
-abandoned:      1          1          1          0                            ok
 -extended:      3    
 -noretire:      1    
     mmaps:      3    
   commits:      2    
   threads:      1          2          4         -2                            ok
  searches:     0.0 avg
numa nodes:       1
   elapsed:       2.001 s
   process: user: 0.001 s, system: 0.000 s, faults: 0, rss: 2.5 MiB, commit: 66.0 MiB

四.接下来对mimalloc-override.h文件做个介绍
改文件用于重写原生API以适应不同平台
下面是一个重写重写和重载的案例

//这里实现对MemPoolMalloc进行重载,判断其传递的参数个数
#define _VA_ARG_NUM(A0,A1,N,...) N

#define VA_ARG_NUM(...) _VA_ARG_NUM(-1,##__VA_ARGS__,1,0)

#define VA_ARG1and2(A0,A1,...) A1

#define MemPoolMalloc(...) ((VA_ARG_NUM(__VA_ARGS__)==0)?mi_malloc(DefaultSize):\
                  (VA_ARG_NUM(__VA_ARGS__)==1)?mi_malloc((VA_ARG1and2(0,##__VA_ARGS__,0))):(9))



#define MemPoolCalloc(n,c)             mi_calloc(n,c)
#define MemPoolRealloc(p,n)            mi_realloc(p,n)
#define MemPoolFree(p)                 mi_free(p)
#define MemPoolInfo(p)                 mi_stats_print(p)
#define MemHeapNew()                   mi_heap_new()
#define MemHeapMalloc(p,c)             mi_heap_malloc(p,c)
#define MemHeapDestory(p)              mi_heap_destroy(p)
 类似资料: