【问题标题】:C++ memory allocation mechanism performance comparison (tcmalloc vs. jemalloc)C++内存分配机制性能对比(tcmalloc vs. jemalloc)
【发布时间】:2011-12-12 18:08:56
【问题描述】:

我有一个分配大量内存的应用程序,我正在考虑使用比 malloc 更好的内存分配机制。

我的主要选择是:jemalloc 和 tcmalloc。使用其中任何一个有什么好处吗?

http://locklessinc.com/benchmarks.shtml中的一些机制(包括作者的专有机制--lockless)有很好的对比 它提到了它们各自的一些优点和缺点。

鉴于这两种机制都很活跃并不断改进。有没有人对这两者的相对表现有任何见解或经验?

【问题讨论】:

  • 你为什么在 C++ 中使用malloc
  • @JohnDibling 性能
  • 我想下一个自然的问题是,你为什么使用 C++?
  • @JohnDibling:我会注意到new 的常见实现依赖于malloc 来获取内存......
  • 您也可以通过简单地减少分配来提高性能。对象池在这里很有帮助。编程可能会有点棘手,但如果分配方案导致性能问题,那么您就应该考虑这一点。

标签: c++ linux malloc tcmalloc


【解决方案1】:

您也可以考虑使用Boehm conservative garbage collector。基本上,您将源代码中的每个malloc 替换为GC_malloc(等等...),并且您不必费心调用free。 Boehm 的 GC 分配内存的速度并不比 malloc 快(大致相同,或者可能会慢 30%),但它具有自动处理无用内存区域的优势,这可能会改进您的程序(并且肯定会简化编码,因为你不再关心免费)。而且Boehm的GC也可以used作为C++分配器。

如果你真的认为malloc 太慢(但你应该进行基准测试;大多数malloc-s 花费不到微秒),并且如果你完全理解程序的分配行为,你可以替换一些 malloc- s 与您的特殊分配器(例如,它可以使用mmap 大块地从内核获取内存并自行管理内存)。但我相信这样做很痛苦。在 C++ 中,您有allocator 概念和std::allocator_traits,大多数标准containers 模板都接受这样的分配器(另见std::allocator),例如std::vector 的可选第二个模板参数等...

正如其他人建议的那样,如果您认为 malloc 是一个瓶颈,您可以按块(或使用 arenas)或仅在数组中分配数据。

有时,实施专门的复制 garbage collector(对于您的某些数据)可能会有所帮助。考虑一下MPS

但不要忘记过早的优化是邪恶的,请对您的应用程序进行基准测试和分析,以准确了解浪费时间的地方。

【讨论】:

  • 我推测迁移到垃圾收集器并不像将malloc更改为gc_malloc那么简单。您还需要将指针的类型更改为某种不透明的句柄类型
  • 不适用于 Boehm 的 GC。它的gc_malloc 旨在替代malloc,并且还提供void*。这是一个保守的GC。
【解决方案2】:

如果我没记错的话,主要区别在于多线程项目。

两个库都试图通过让线程从不同的缓存中挑选内存来消除内存争用,但它们有不同的策略:

  • jemalloc(由 Facebook 使用)为每个线程维护一个缓存
  • tcmalloc(来自 Google)维护一个缓存池,线程对缓存产生“自然”亲和力,但可能会发生变化

如果我没记错的话,这又导致了线程管理方面的一个重要差异。

  • 如果线程是静态的,例如使用池,jemalloc 会更快
  • tcmalloc 在创建/销毁线程时更快

还有一个问题是,由于jemalloc 会旋转新缓存以容纳新的线程 ID,因此线程的突然激增将使您在随后的平静阶段(大部分)缓存为空。

因此,我建议在一般情况下使用tcmalloc,并将jemalloc 保留用于非常具体的用途(应用程序生命周期内线程数的变化很小)。

【讨论】:

    【解决方案3】:

    请注意,根据“nedmalloc”主页,现代操作系统的分配器现在实际上非常快:

    “Windows 7、Linux 3.x、FreeBSD 8、Mac OS X 10.6 都包含最先进的分配器,并且没有第三方分配器可能在现实世界的结果中显着改善它们”

    http://www.nedprod.com/programs/portable/nedmalloc

    因此,您可能只需建议您的用户升级或类似的东西就可以侥幸:)

    【讨论】:

    • 这也是我的观察。以及 CRT 开发人员的观察,因为今天的标准 malloc 只是 win32 中的 VirtualAlloc 和 linux 中的 mmap 的包装器。
    • @v.oddou: 所有malloc 实现最终都会在某个时候调用mmap——因为这是从系统获取内存的唯一方法(几乎)。 mmap 的问题在于它无论如何都很慢(这是一个涉及上下文切换的系统调用),并且它只能以页面粒度(4K 或更大)分配内存。 malloc 实现的目标是使用 mmap 从系统中获取大块内存,然后从用户进程中在这些内存区域中分配较小的对象。
    • @rogerdpack:注意jemalloc FreeBSD 的分配器。
    • @YakovGalka 在没有明确研究的情况下,我将发出不确定性的免责声明。但是根据我目前的知识,您的信息是错误的或过时的。一些旧的 malloc 使用 srbk 从系统中获取块。而mmap 是大型独立区块的后备方案。今天它被直接调用,而不是“在某个时候”,malloc 实现现在是空的。他们从字面上包装 mmap 没有礼节。系统调用也有快速陷阱。
    • @v.oddou 我真诚地建议您查看任何“空的现代 malloc 实现”的来源,并弄清楚为什么包装“mmap”涉及数千行代码。
    【解决方案4】:

    我最近考虑将 tcmalloc 用于一个工作项目。这是我观察到的:

    • 大大提高了在多线程设置中大量使用 malloc 的性能。我在工作中将它与一个工具一起使用,性能几乎提高了两倍。原因是在这个工具中,有几个线程在关键循环中执行小对象的分配。使用 glibc,性能会受到影响,因为我认为不同线程中 malloc/free 调用之间的锁争用。

    • 不幸的是,tcmalloc 增加了内存占用。我上面提到的工具会消耗两到三倍的内存(以最大驻留集大小衡量)。增加的内存占用对我们来说是行不通的,因为我们实际上正在寻找减少内存占用的方法。

    最后我决定不使用 tcmalloc 而是直接优化应用程序代码:这意味着从内部循环中删除分配以避免 malloc/free 锁争用。 (出于好奇,使用一种压缩形式而不是使用内存池。)

    给您的教训是,您应该仔细衡量具有典型工作负载的应用程序。如果您负担得起额外的内存使用量,那么 tcmalloc 对您来说可能会很棒。如果没有,tcmalloc 仍然有助于查看通过避免频繁调用跨线程内存分配会获得什么。

    【讨论】:

      【解决方案5】:

      你的帖子没有提到线程,但是在考虑混合 C 和 C++ 分配方法之前,我会研究内存池的概念。BOOST 有一个很好的。

      【讨论】:

      • 谢谢。首先,这看起来不错。我会看看它。其次,我处于分析和优化阶段,并优化瓶颈(这里是内存分配)。三、混合C/C++分配方式有什么问题吗? (除了使代码变脏/不标准)。
      【解决方案6】:

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-31
      • 2011-08-14
      • 2017-11-22
      • 1970-01-01
      • 2018-11-18
      • 2012-04-09
      • 2011-01-31
      相关资源
      最近更新 更多