【问题标题】:Does exporting everything from DLL affect performance从 DLL 导出所有内容是否会影响性能
【发布时间】:2016-04-04 13:52:41
【问题描述】:

为了进行单元测试,我必须导出许多小的内部类,这些类从未打算供我的 DLL 的客户端使用。

我知道每个导出的函数都会在可执行映像中产生一个存根,如果 DLL 未在其首选位置加载,Windows 加载程序必须对这些存根执行修复。

有人建议将 DLL 构建为静态库,仅用于单元测试。

我想知道这是否值得麻烦?我找不到任何关于从 DLL 导出每个类的问题可能有多大的参考,或者如果我有选择性的话,加载器性能和内存消耗是否有任何显着提升。

我想我在某处读到 GCC 编译器默认导出所有内容。

编辑:由于该问题的陈述动机是有争议的,让我改写一下: 我是否应该检查我的 DLL 并删除所有未向其客户端公开的类上的 DLLEXPORT?假设我正在使用一堆遗留 DLL,我注意到它们有很多不必要的导出。这会提高加载速度吗?特别是在使用 MSVC 版本 9+ 的 Windows 7 和 8 上。

【问题讨论】:

  • 这是一种代码味道。如果您可以检测到您的代码失败的唯一方法是访问内部,那么普通的客户端程序员怎么能检测到它呢?或者你只是在测试错误的东西,只测试客户端程序员将使用的公共 api。
  • @HansPassant 我们正在做 TDD - 自下而上构建,管理层的要求是每一层代码都必须有一组单元测试,即使它不直接暴露给客户端.事实上,我们并不确切知道最终的公共 API 会是什么。
  • 不太可能造成可衡量的性能损失。但你总是可以试试看。设置也应该很容易,以便只有调试版本包含这些导出。
  • 是的,但如果我错了,请纠正我,它们都是按序号导出的,因此它减少了 dll 大小,并且在绑定时不会发生查找/字符串搜索。好文章:blog.omega-prime.co.uk/?p=115
  • 显而易见的“成本”是您的 DLL 会因两个原因而变大。首先,链接器不能丢弃这些类的任何成员函数,因为它们都将被使用(通常,它可以丢弃任何未调用或完全内联的成员函数)。其次,导出表将大得多,因为它将包含所有附加导出的修饰名称字符串。较大的 DLL 会消耗更多的地址空间。我预计除了大小开销之外,不会有太多可衡量的性能影响。

标签: windows performance dll


【解决方案1】:

从 DLL 导出所有内容会影响性能吗?

它可能会,但是效果非常小。我制作了一个 python 脚本,它创建了一个导出 > 50,000 个符号的测试 DLL。它由 1024 个导出的类组成,每个类包含 48 个函数(16 个成员函数、16 个虚拟函数和 16 个静态函数)。编译器还会为每个看起来像 vtable 的类生成大约 4-5 个导出。

我使用 SysInternals ProcMon 测量了应用程序的加载时间。在链接 DLL 之前,非常古老的动力不足的测试机器上的加载时间在 15-30 毫秒之间。 添加 DLL,并调用约 50,000 个导出函数中的每一个,导致没有可测量的变化

这不是一个完全决定性的测试,但足以让我相信符号分辨率和修复可能比任何其他限制因素快一个数量级或更快

有趣的是,为了能够使用 Microsoft 工具创建这样一个疯狂的 DLL,需要添加 /bigobj 编译器标志,而且在 DLL 的 PE 格式中导出符号似乎也有 64K 的限制。此外,DLL 和应用程序的静态(编译时)编译和链接阶段每个都需要很长时间并占用大量内存。

因此,在遇到加载程序性能问题之前,您将突破各种其他限制。

假设我正在使用一堆旧版 DLL,我注意到它们有很多不必要的导出。这会提高加载速度吗?

没有。

我是否应该检查我的 DLL 并删除所有未向其客户端公开的类上的 DLLEXPORT?

视情况而定。

不只是因为负载性能。如果这对应用程序如此重要,那么大概有人会对启动进行基准测试并知道确切性能问题在哪里.我们不应该猜测性能影响:

“我们应该忘记小的效率,比如大约 97% 的时间:过早的优化是万恶之源。” -- 克努特

但是,不导出这些“内部”类和函数可能还有其他原因。 导出类/函数的目的是让客户端代码可以使用它。它应该与 DLL 的逻辑外部 API 匹配。如果函数或类不是这种情况,那么它不应该被导出。如果内部类中有很多功能在不通过 DLL 的公共 API 的情况下无法使用或测试,那么人们会想知道为什么存在该功能?如果目的是创建通用的可重用类,也许它们应该在自己的库中?

测试驱动设计并不意味着您必须公开公开所有内容。即使是对一个类最具侵入性的白盒单元测试,也不一定需要 DLL 导出。例如,可以单片构建单元测试夹具,并静态链接(甚至直接包含源代码)到所需的任何内部类。

相反,以这种方式完成它的一个完全可以原谅的解释可能只是它易于实施。如果其他一切都基本相同(模样式和一些理论架构问题),那么不必要地更改和破坏已经以某种方式完成并且运行良好的系统也是一种糟糕的形式。

所以这可能是一个不应该被复制或扩展的设计,也许在维护或重构机会出现时清理它是值得的。

我想我在某处读到 GCC 编译器默认导出所有内容。

Mingw LD documentation 同意。不过,请注意,如果您使用 __declspec 或 .DEF 文件导出,此自动导出行为将被禁用。

【讨论】:

  • +1。感谢您进行实际测试。您能否也发布时间和/或测试脚本,以便我们重现它?尽管如此,这听起来很明显:绑定可能在哈希表中,因此添加方法不会增加性能损失是有道理的。我个人对 DLL 与静态库的开销更感兴趣:从编译器 POV 来看,您可以对静态库进行比 DLL 更多的优化。虽然我同意这不是问题所在,但我想知道使用您当前的测试脚本是否容易测试?
  • 你打败了我!我开始做同样的事情。我也在考虑向 The Old new Thing 的 Raymond Chen 发送问题,因为他喜欢挖掘这些“旧”事物。坦率地说,我没想到会有什么大的影响,但是一旦我提出这个问题,我有点不高兴,很难得到基于实验/经验或权威的确认。我的固执让我丢了 200 分,但你确实活该。
猜你喜欢
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 1970-01-01
  • 2018-03-20
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多