静态链接与动态链接

 ̄綄美尐妖づ 提交于 2020-02-27 23:47:32

在某些情况下,是否有任何令人信服的性能原因选择静态链接而不是动态链接? 我已经听过或阅读了以下内容,但我对这个主题的了解不足以保证其准确性。

1)静态链接和动态链接之间的运行时性能差异通常可以忽略不计。

2)(1)如果使用使用概要文件数据来优化程序热路径的性能分析编译器,则不正确,因为使用静态链接,编译器可以同时优化您的代码和库代码。 使用动态链接只能优化您的代码。 如果大部分时间都花在运行库代码上,那将有很大的不同。 否则,(1)仍然适用。


#1楼

  • 动态链接可以减少总资源消耗 (如果多个进程共享同一个库(当然,包括“相同”中的版本))。 我相信这是促使它在大多数环境中都存在的论点。 这里的“资源”包括磁盘空间,RAM和缓存空间。 当然,如果动态链接程序的灵活性不足,则存在DLL hell的风险。
  • 动态链接意味着可以传播错误修复和库升级,以改进您的产品而无需运送任何物品。
  • 插件始终要求进行动态链接。
  • 静态链接意味着您可以知道代码将在非常有限的环境中运行(在启动过程中或在救援模式下)。
  • 静态链接可以使二进制代码更易于分发到不同的用户环境(以发送更大,更占用资源的程序为代价)。
  • 静态链接可能会稍微加快启动时间,但这在某种程度上取决于程序的大小和复杂性以及操作系统的加载策略的详细信息。

进行一些编辑以在评论和其他答案中包含非常相关的建议。 我想指出的是,您要采用的中断方式在很大程度上取决于您计划在何种环境下运行。最小的嵌入式系统可能没有足够的资源来支持动态链接。 稍大的小型系统可能会很好地支持动态链接,因为它们的内存足够小,可以使动态链接节省的RAM非常吸引人。 正如Mark所指出的那样,功能强大的消费类PC拥有大量资源,您可能会让便利问题驱使您对此事进行思考。


解决性能和效率问题: 取决于

传统上,动态库需要某种胶合层,这通常意味着在函数寻址中需要双重调度或额外的间接层,并且可能花费一点速度(但是函数调用时间实际上是您运行时间的很大一部分吗?)。

但是,如果您运行的多个进程都多次调用同一个库,则相对于使用静态链接而言,使用动态链接时,最终可以节省缓存行(从而赢得运行性能)。 (除非现代操作系统足够聪明,以至于可以注意到静态链接的二进制文件中的相同段。似乎很难,有人知道吗?)

另一个问题:加载时间。 您需要在某些时候支付加载费用。 何时付款,取决于操作系统的工作方式以及所使用的链接。 也许您宁愿推迟付款,直到您知道自己需要它为止。

注意,传统上,静态-动态-动态链接不是优化问题,因为它们都涉及到目标文件的单独编译。 但是,这不是必需的:编译器原则上可以最初将“静态库”“编译”为摘要的AST形式,然后通过将这些AST添加到为主代码生成的AST中来“链接”它们,从而实现全局优化。 我使用的系统都没有做到这一点,因此我无法评论它的运行情况。

回答性能问题的方法始终是通过测试(并尽可能使用测试环境,就像部署环境一样)。


#2楼

我同意dnmckee提到的要点,此外:

  • 静态链接的应用程序可能更易于部署,因为很少或没有其他文件依赖项(.dll / .so),当它们丢失或安装在错误的位置时,它们可能会引起问题。

#3楼

本章详细讨论了Linux上的共享库及其性能影响。


#4楼

1)基于以下事实:调用DLL函数始终使用额外的间接跳转。 如今,这通常可以忽略不计。 在DLL内,i386 CPU的开销更大,因为它们无法生成位置无关的代码。 在amd64上,跳转可以相对于程序计数器,因此这是一个巨大的改进。

2)这是正确的。 通过性能分析进行优化,通常可以赢得大约10-15%的性能。 现在,CPU速度已达到极限,可能值得这样做。

我要补充一点:(3)链接器可以按更有效的缓存分组方式安排功能,从而使昂贵的缓存级别未命中率降至最低。 它还可能特别影响应用程序的启动时间(基于我在Sun C ++编译器中看到的结果)

并且不要忘记,使用DLL不会导致死代码消除。 取决于语言,DLL代码也可能不是最佳选择。 虚函数始终是虚函数,因为编译器不知道客户端是否覆盖它。

由于这些原因,如果根本不需要DLL,则只需使用静态编译即可。

编辑(回答评论,按用户下划线)

这是有关位置独立代码问题的好资源, http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

如前所述,x86在15位跳转范围内没有其他任何条件的AFAIK,并且没有无条件跳转和调用的条件。 这就是为什么(来自发电机)具有超过32K的功能始终是一个问题,需要嵌入式蹦床。

但是在像Linux这样的流行x86操作系统上,您只需要关心是否不是使用gcc开关-fpic (强制使用间接跳转表)生成的SO / DLL文件。 因为如果您不这样做,代码将像普通链接器将其重新定位一样被固定。 但是这样做时,它会使代码段不可共享,并且需要将代码从磁盘到内存的完整映射,并触摸所有内容,然后才能使用它(清空大部分缓存,击中TLB),等等。当这被认为是缓慢的...太慢了。

因此,您将不再有任何好处。

我不记得是什么操作系统(Solaris或FreeBSD)给我的Unix构建系统带来了问题,因为我没有这么做,并且想知道为什么在我将-fPIC应用于gcc之前它崩溃了。


#5楼

进行静态链接生成的原因之一是要验证您完全关闭了可执行文件,即,正确解析了所有符号引用。

作为使用持续集成构建和测试的大型系统的一部分,夜间回归测试是使用可执行程序的静态链接版本运行的。 有时,即使动态链接的可执行文件可以成功链接,我们也会看到符号无法解析并且静态链接将失败。

这通常发生在共享库深处的符号具有拼写错误的名称且因此不会静态链接的情况下。 无论使用深度优先还是宽度优先评估,动态链接程序都不会完全解析所有符号,因此您可以完成一个没有完全关闭的动态链接的可执行文件。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!