mimalloc简单的使用

最近实现一个多线程内存池,所以看了很多开源的代码,其中微软的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)
### mimalloc 内存分配器介绍 mimalloc 是由微软开发并开源的一款高性能、轻量级的通用内存分配器,主要用于提升程序运行时的内存管理效率[^1]。该工具以其卓越的速度表现著称,在多个基准测试中超越了诸如 jemalloc、tcmalloc 和 Hoard 这样的知名竞争对手。 #### 核心优势 - **高性能**:相比传统方案,mimalloc 提供更快的分配速度以及更低延迟。 - **低内存开销**:仅需约 0.2% 的额外存储用于维护结构体信息,并有效减少内部碎片化现象。 - **无锁机制**:采用原子指令代替互斥锁控制并发访问,从而减轻多线程环境下资源争用问题。 - **安全性增强**:内置防护措施防止常见类型的缓冲区溢出攻击;提供随机化布局选项增加恶意利用难度。 - **灵活度高**:允许创建独立的工作集(heap),方便隔离不同部分的数据处理逻辑。 - **跨平台兼容性强**:能够无缝对接 Linux, Windows 等主流操作系统环境下的各类应用程序框架。 --- ### 使用方法概述 为了充分利用 mimalloc 所带来的效能改进,开发者可以通过如下方式将其融入现有工程: ```cpp // 替代标准库函数定义头文件 #include "mimalloc-override.h" ``` 上述代码片段展示了如何引入 `mimalloc` 来替代系统的默认内存分配接口。一旦完成此设置,则后续所有的动态申请操作都将自动交由更高效的算法接管执行[^5]。 对于那些希望保留原有 API 调用形式而不改变业务层编码风格的情况来说,这种方法尤为适用。此外,还存在专门针对 C++ 用户准备的一组宏定义——即 `"mimalloc-new-delete.h"` ——它使得重载 new/delete 成为可能,进一步简化移植过程中的工作量。 当涉及到具体对象实例化环节时,程序员只需按照常规流程编写即可,无需担心底层细节变化所带来的影响。例如: ```cpp int* array = (int*)mi_malloc(sizeof(int)*10); // 显式调用自定义版本malloc() delete[] array; // 利用了C++特性的析构语句同样生效 ``` 值得注意的是,在某些特殊场景下或许有必要手动指定参数来自定义行为模式,比如开启诊断日志记录或是调整缓存大小等高级设定项。此时则建议查阅官方文档获取更多指导说明[^4]。 --- ### 性能特点分析 实验数据显示,在 Release 构建条件下,无论是小型还是大型请求序列,mimalloc 均展现出明显优于系统自带的新建/释放命令的表现趋势。这得益于其精心设计的核心架构与优化策略,包括但不限于紧凑型数据表示法、快速路径优先原则以及细粒度锁定消除技术等方面的努力成果。 然而需要注意的是,如果启用了 Debug 版本编译开关的话,那么出于调试目的而附加进去的日志输出和其他辅助设施将会拖慢整体响应时间,因此不推荐在生产环境中部署此类配置。 综上所述,凭借出色的吞吐能力和稳定性保障,mimalloc 已经成为众多追求极致性能体验的技术团队首选之一。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶与花语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值