要进行单元测试,我必须导出许多小型内部类,这些类从未被我的DLL客户端使用。
我知道每个导出的函数都会在可执行映像中生成存根,并且如果DLL未在其首选位置加载,则Windows加载程序必须对这些存根执行修复。
有人建议将DLL构建为静态库,仅用于单元测试。
我想知道这是值得的吗?我没有找到任何关于从DLL导出每个类的问题有多重要的参考,或者如果我对它有选择性,那么在加载器性能和内存消耗方面是否有任何显着的增益。
我想我在某处读到GCC编译器默认导出所有内容。
编辑 :既然问题的陈述动机是有争议的,那么让我重新说一下: 我是否应该浏览我的DLL并删除所有未向其客户端公开的类的DLLEXPORT?假设我正在使用一堆遗留的DLL,我注意到它们有很多不必要的导出。这会提高装载速度吗?特别是在Windows 7和8上使用MSVC版本9 +。
答案 0 :(得分:5)
从DLL导出所有内容会影响性能吗?
它可能确实如此,但效果是无法估量的。我创建了一个python脚本,用于创建测试DLL导出> 50,000个符号。它由1024个导出的类组成,每个类包含48个函数(16个成员函数,16个虚函数和16个静态函数)。编译器还为每个类生成大约4-5个导出,看起来像vtable。
我使用SysInternals ProcMon测量了应用程序的加载时间。在链接DLL之前,非常古老的动力不足的测试机器的加载时间在15-30ms之间。 添加DLL,并且对~50,000个导出函数中的每个函数进行一次调用后,无法进行任何可衡量的更改 。
这不是一个完全确定的测试,但它足以说服我 符号分辨率和修正可能比任何其他限制因素快一个数量级或更快 强>
有趣的是,为了能够使用Microsoft工具创建这样一个疯狂的DLL,需要添加/ bigobj编译器标志,并且看起来在DLL的PE格式中也存在64K导出符号的限制。此外,DLL和应用程序的静态(编译时)编译和链接阶段每个花费很多分钟并使用了大量内存。
因此,在遇到装载机性能问题之前,您将推进各种其他限制。
让我们说我正在使用一堆遗留的DLL,我注意到它们有很多不必要的导出。这会提高装载速度吗?
不。
我应该浏览我的DLL并删除所有未向其客户端公开的类的DLLEXPORT吗?
取决于。
不仅仅是因为负载性能。如果这对应用程序如此重要,那么可能有人会对启动进行基准测试,并且会知道完全性能问题在哪里。我们不应该猜测性能影响:
"我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源。" - Knuth
但是,可能还有其他原因导致不能导出这些"内部"类和函数。导出类/函数的意义在于客户端代码可以使用它。它应该与DLL的逻辑外部API匹配。如果不是函数或类的情况,那么它就不应该被导出。如果内部类中有许多功能无法使用或测试,而无需通过DLL的公共API,那么就会让人怀疑为什么存在该功能?如果意图是创建通用的可重用类,也许它们应该在自己的库中?
测试驱动设计并不意味着您必须公开展示所有内容。即使是最具侵入性的白盒单元测试,也不一定需要DLL导出。例如,单元测试夹具可以单片构建,并静态链接(甚至直接包括源)到任何需要的内部类。
相反,以这种方式完成的完全可原谅的解释可能只是简单易行且易于实现。如果其他一切基本相同(模数式和一些理论架构问题),那么不必要地改变和破坏已经以某种方式完成并且工作正常的系统也是不好的形式。
所以这可能是一个不应该复制或扩展的设计,也许值得在维护或重构机会出现时进行清理。
我想我在某处读到GCC编译器默认导出所有内容。
Mingw LD documentation同意。虽然,请注意,如果您使用__declspec或.DEF文件导出,则会禁用此自动导出行为。