我有一个分配大量内存的应用程序,我正在考虑使用比malloc更好的内存分配机制。
我的主要选择是:jemalloc和tcmalloc。使用它们中的任何一个比使用另一个有什么好处吗?
中的一些机制(包括作者的专有机制——lockless(之间有很好的比较http://locklessinc.com/benchmarks.shtml它还提到了它们各自的优点和缺点。
鉴于这两个机制都是积极的,并且不断改进。有人对这两者的相对表现有任何见解或经验吗?
如果我没记错的话,主要区别在于多线程项目。
两个库都试图通过让线程从不同的缓存中提取内存来消除争用内存,但它们有不同的策略:
jemalloc
(Facebook使用(为每个线程维护一个缓存tcmalloc
(来自Google(维护了一个缓存池,线程对缓存产生了"自然"的亲和力,但可能会发生变化
如果我没有记错的话,这再次导致了线程管理方面的一个重要差异。
- 如果线程是静态的,例如使用池,
jemalloc
会更快 - 创建/销毁线程时
tcmalloc
速度更快
还有一个问题是,由于jemalloc
会旋转新的缓存以容纳新的线程ID,因此在随后的平静阶段,突然出现线程高峰会使缓存(大部分(为空。
因此,我建议在一般情况下使用tcmalloc
,并将jemalloc
保留用于非常特定的用途(在应用程序的生命周期内线程数变化较小(。
我最近考虑将tcmalloc用于一个正在工作的项目。这就是我观察到的:
-
在多线程设置中大量使用malloc大大提高了性能。我在工作中把它和一个工具一起使用,性能几乎提高了两倍。原因是在这个工具中,有几个线程在关键循环中执行小对象的分配。使用glibc,性能会受到影响,我认为这是因为不同线程中malloc/free调用之间的锁争用。
-
不幸的是,tcmalloc增加了内存占用。我上面提到的工具将消耗两到三倍的内存(根据最大驻留集大小来衡量(。增加内存占用对我们来说是不可能的,因为我们实际上正在寻找减少内存占用的方法。
最后,我决定不使用tcmalloc,而是直接优化应用程序代码:这意味着从内部循环中删除分配,以避免malloc/free锁冲突。(对于好奇的人来说,使用一种压缩形式,而不是使用内存池。(
给你的教训是,你应该仔细地用典型的工作负载来衡量你的应用程序。如果你能负担得起额外的内存使用,tcmalloc对你来说可能很好。如果没有,tcmalloc仍然可以帮助您了解避免频繁调用跨线程的内存分配会带来什么好处。
请注意,根据"nedmalloc"主页,现代操作系统的分配器现在实际上相当快:
"Windows 7、Linux 3.x、FreeBSD 8、Mac OS x 10.6都包含最先进的分配器,在现实世界中,没有第三方分配器能够显著改进它们。">
http://www.nedprod.com/programs/portable/nedmalloc
因此,您可能只需要推荐用户升级或类似的东西就可以逃脱惩罚:(
您也可以考虑使用Boehm保守垃圾收集器。基本上,您可以用GC_malloc
(等等(替换源代码中的每个malloc
,并且不必调用free
。Boehm的GC分配内存的速度并不比malloc快(大约相同,或者可能慢30%(,但它的优势是可以自动处理无用的内存区域,这可能会改进您的程序(当然也会简化编码,因为您不再关心免费(。Boehm的GC也可以用作C++分配器。
如果你真的认为malloc
太慢(但你应该进行基准测试;大多数malloc
-s的时间不到微秒(,并且如果你完全理解程序的分配行为,你可以用你的特殊分配器来替换一些malloc(例如,它可以使用mmap
从内核中获取大块内存,并自己管理内存(。但我相信这样做是一种痛苦。在C++中,您有分配器概念和std::allocator_traits
,大多数标准容器模板都接受这样的分配器(另请参见std::allocator
(,例如std::vector
的可选第二个模板参数等
正如其他人所建议的,如果您认为malloc
是一个瓶颈,您可以按块(或使用arena(或仅按数组分配数据。
有时,实现一个专门的复制垃圾收集器(用于某些数据(可能会有所帮助。也许可以考虑MPS。
但不要忘记,过早优化是邪恶的,请基准&分析您的应用程序以准确了解时间损失的位置。
这里有一个关于分配器的很好的讨论:
http://www.reddit.com/r/programming/comments/7o8d9/tcmalloca_faster_malloc_than_glibcs_open_sourced/
您的文章没有提到线程,但在考虑混合C和C++分配方法之前,我会研究内存池的概念。BOOST有一个很好的。