【问题标题】:Windows malloc replacement (e.g., tcmalloc) and dynamic crt linkingWindows malloc 替换(例如 tcmalloc)和动态 crt 链接
【发布时间】:2010-10-25 21:47:07
【问题描述】:

使用多个 DLL 和 QT 的 C++ 程序应配备 malloc 替代品(如 tcmalloc),以解决可验证为由 Windows malloc 引起的性能问题。 linux 没问题,windows 有好几种方法,我觉得没有一个吸引人:

1.将新的 malloc 放入 lib 并确保首先链接它(Other SO-question)

这有一个缺点,例如strdup will still use the old malloc and a free may crash the program

2。使用 lib.exe (Chrome) 从静态 libcrt 库中删除 malloc

这是为chrome/chromium 测试/使用的(?),但缺点是它仅适用于静态链接crt。如果一个系统库与 msvcrt 动态链接,则静态链接会出现问题,可能有mismatches in the heap allocation/deallocation。如果我理解正确,tcmalloc 可以动态链接,这样所有自编译的 dll 都有一个公共堆(这很好)。

3.补丁crt-源码(firefox)

Firefox's jemalloc 显然修补了 windows CRT 源代码并构建了一个新的 crt。这又出现了上面的静态/动态链接问题。

可以考虑使用它来生成动态 MSVCRT,但我认为这是不可能的,因为许可证禁止提供具有相同名称的修补 MSVCRT。

4.在运行时动态修补加载的 CRT

一些商业内存分配器可以做到这一点。 tcmalloc 也可以,但这看起来相当难看。它有一些问题,但它们已得到修复。目前,tcmalloc 不能在 64 位窗口下工作。

有更好的方法吗?有cmets吗?

【问题讨论】:

  • 那么您使用了哪种方法?您使用哪一个来验证备用分配器比 CRT malloc 提供的分配器工作得更好的断言?您使用了哪个版本的 CRT,它与新版本更好/更差/相同吗?
  • 为什么不尝试替换全局 C++ 新的呢?这不可行(并匹配共享库 + 应用程序主二进制文件 + ms crt 作为共享库设置)?

标签: windows dll malloc msvcrt crt


【解决方案1】:

问:跨多个 dll 拆分的 C++ 程序应该:

A) 替换 malloc?

B) 确保分配和解除分配发生在同一个 dll 模块中?

A:正确答案是 B。包含多个 DLL 的 c++ 应用程序设计应该确保存在一种机制,以确保在一个 dll 中分配在堆上的东西被同一个 dll 模块释放。


你为什么要把一个 c++ 程序分成几个 dll 呢?通过 c++ 程序,我的意思是您正在处理的对象和类型是 c++ 模板、STL 对象、类等。如果没有大量非常仔细的设计和大量编译器特定的魔法,或者遭受痛苦,您就无法跨 dll 边界传递 c++ 对象由于各种 dll 中目标代码的大量重复,因此应用程序对版本非常敏感。对类定义的任何微小更改都将强制重新构建所有 exe 和 dll,从而消除 dll 方法对应用程序开发的至少一项主要优势。

要么坚持应用程序和 dll 之间的直接 C 接口,受苦受难,要么将整个 c++ 应用程序编译为一个 exe。

【讨论】:

    【解决方案2】:

    这是一个大胆的主张,即 C++ 程序“应该配备 malloc 替代品(如 tcmalloc)以解决性能问题......”

    “[在] 8 个流行基准测试中的 6 个 ... [真实大小的应用程序] 用系统提供的哑分配器替换了人们在其中投入了大量时间和金钱的自定义分配器[产生] 更好的性能......最简单的自定义分配器,针对非常特殊的情况进行了调整,是唯一可以提供收益的分配器。” --Andrei Alexandrescu

    大多数系统分配器与通用分配器差不多。 只有如果你有一个非常具体的分配模式,你可以做得更好。

    通常,此类特殊模式仅适用于程序的一部分,在这种情况下,最好将自定义分配器应用于可以受益的特定部分,而不是全局替换分配器。

    C++ 提供了几种选择性替换分配器的方法。例如,您可以为 STL 容器提供分配器,或者您可以逐个类地覆盖 new 和 delete。与全局替换分配器的任何 hack 相比,这两者都能为您提供更好的控制。

    还请注意,替换 malloc 和 free 不一定会更改运算符 new 和 delete 使用的分配器。虽然全局 new 运算符通常使用 malloc 实现,但并不要求它这样做。所以替换 malloc 甚至可能不会影响大部分分配。

    如果您使用的是 C,您可以在重要的地方使用自定义分配器包装或替换密钥 malloc 和 free 调用,并让程序的其余部分使用默认分配器。 (如果不是这样,您可能需要考虑进行一些重构。)

    系统分配器背后有数十年的发展。它们稳定且经过良好测试。它们在一般情况下表现得非常好(在原始速度、线程争用和碎片方面)。他们有用于泄漏检测的调试版本和对跟踪工具的支持。有些甚至通过提供针对堆缓冲区溢出漏洞的防御来提高应用程序的安全性。很有可能,您要使用的库仅使用系统分配器进行了测试。

    大多数替代系统分配器的技术丧失了这些优势。在某些情况下,它们甚至会增加内存需求(因为它们不能与其他进程可能使用的 DLL 运行时共享)。面对编译器版本、运行时版本甚至操作系统版本的变化,它们也往往非常脆弱。使用经过调整的运行时版本会阻止您的用户从操作系统供应商处获得运行时更新的好处。当您可以通过将自定义分配器应用到程序中可以从中受益的特殊部分来保留这些好处时,为什么要放弃所有这些?

    【讨论】:

      【解决方案3】:

      您的前提“使用多个 DLL 和 QT 的 C++ 程序应该配备 malloc 替代品”从何而来?

      在 Windows 上,如果所有 dll 都使用共享的 MSVCRT,则无需替换 malloc。默认情况下,Qt 针对共享的 MSVCRT dll 构建。

      如果他们遇到问题:

      1) 混合使用静态链接与使用共享 VCRT 的 dll

      2) AND 还释放未在其来源处分配的内存(即,由共享 VCRT 分配的静态链接 dll 中的释放内存,反之亦然)。

      请注意,在资源周围添加您自己的引用计数包装器可以帮助缓解与需要以特定方式释放的资源相关的问题(即,通过回调原始 dll 来处理一种类型资源的包装器,来自另一个 dll 的资源的不同包装器等)。

      【讨论】:

      • 前提是使用 tcmalloc 代替 MSVCRT 时性能大幅提升。
      • 如果是这种情况,那么一个 C 程序甚至一个不使用 Qt 的 C++ 程序都可以从这个变化中受益。但是,我不同意他们“应该配备 malloc 替代品”,直到分析表明 MSVCRT 不足。
      • 1.测量到的大幅性能改进显然与 MSVCRT 的内存管理有关。影响取决于程序的分配方式,因此我并不认为 MSVCRT 不好。 2.我刚才提到Qt是为了表明有几个子应用程序使用QT-lib,因此静态链接不是首选。
      • 仔细阅读:我说的是“A 程序”而不是“C++ 程序”。 MSVCRT 对堆有特殊的小对象处理,但据我了解,它具有固定的最大小对象大小。对于特定的程序,这表现得很糟糕。
      • 更换系统内存分配策略通常是可取的,因为所述系统内存分配器与替代方案(例如我已广泛使用的 dlmalloc)相比具有性能特征。显然,不需要内存分配并且只使用静态池可以解决这个问题,但是如果一个人正在继承执行大量内存分配并调用 malloc() 和朋友的代码,则通常可以通过使用替代方案来提高性能。对于许多应用程序来说,默认值可能就足够了,但它绝不是性能领先者。
      【解决方案4】:

      nedmalloc?还要注意 smplayer 使用一个特殊的补丁来覆盖 malloc,这可能是你前进的方向。

      【讨论】:

      • 你知道,nedmalloc 是如何处理这个问题的吗?
      • 不确定,虽然我知道它“不会自动替换系统 malloc()”nedprod.com/programs/portable/nedmalloc 可能有更多
      • 在nedmalloc源代码中,有一个winpatcher。看起来它正在发挥 (4) 的魔力,这可能就是你想要的。不过,尚未检查是否支持 x64 窗口。
      猜你喜欢
      • 2018-05-04
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 2013-12-13
      • 1970-01-01
      • 2022-12-05
      相关资源
      最近更新 更多