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

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

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

评论

“使用静态链接,编译器可以优化..库代码”,但前提是必须进行编译!如果仅链接到预编译的目标文件,则编译器将没有机会对其进行优化。

如果这是真的,那您是对的,但是对于现代编译器的真实性,还有一个问题,如果有人可以以一种或另一种方式进行验证,那将是很好的。

使用编译器编译为本机代码(就像大多数C / C ++编译器一样),就没有更多的机会进行代码优化了。如果将代码编译为某种中间语言(例如.Net IL),则在加载库时将调用JIT编译器以将其编译为本机代码。随着JIT编译器的发展,最终编译会随着时间的推移变得越来越好。

@Eloff:VS2008在启用LTCG的情况下完全可以做到这一点。 (但是lib文件变得很大。)我一直在玩弄它,对于一个对“我的编译器能为我做些什么”感兴趣的人来说,这简直就是惊人。

#1 楼



动态链接可以减少总资源消耗(如果多个进程共享同一个库(当然,包括“相同”中的版本))。我相信这是促使它在大多数环境中都存在的论点。这里的“资源”包括磁盘空间,RAM和缓存空间。当然,如果动态链接程序的灵活性不足,则存在DLL崩溃的危险。 />
插件始终要求动态链接。

静态链接可以使二进制文件更容易分发到不同的用户环境(以发送更大,更耗资源的程序为代价)。时间,但这在某种程度上取决于程序的大小和复杂性以及操作系统的加载策略的详细信息。和其他答案。我想指出的是,您要采用的中断方式在很大程度上取决于您计划在何种环境下运行。最小的嵌入式系统可能没有足够的资源来支持动态链接。稍大的小型系统可能会很好地支持动态链接,因为它们的内存足够小,可以使动态链接节省的RAM非常吸引人。正如Mark所指出的那样,功能强大的消费类PC拥有大量资源,您也许可以让便利问题驱使您对此事进行思考。 。

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

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

另一个问题:加载时间。您需要在某些时候支付加载费用。何时付款,取决于操作系统的工作方式以及所使用的链接。

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

评论


资源消耗基本上是代码空间,随着时间的流逝,它变得越来越少。如果在5个进程之间共享500K的库,则可节省2MB,不到3GB RAM的0.1%。

– Mark Ransom
2010年1月3日,下午4:57

如果该库还共享相同的虚拟映射(所有进程中具有相同的物理和虚拟地址),那么动态链接是否也将TLB插槽保存在处理器的MMU中?

–赞·山猫
2010年1月3日,下午6:07

动态链接还使您可以轻松地用更好的版本更新错误的库代码。

–赞·山猫
2010年1月3日,下午6:07

@Zan还可以轻松地将错误代码添加到工作版本中。

– anon
2010年1月4日,19:17



“插件总是要求动态链接。”那是不对的。某些插件模型(例如Apple的AudioUnits)可以在单独的过程中运行插件并使用IPC。这是动态链接插件的更安全选择(插件不会使主机崩溃)。建议将答案更新为“插件可能需要动态链接”或类似内容。

–泰勒
2015年10月12日,0:01

#2 楼

动态链接是满足某些许可证要求(例如LGPL)的唯一实用方法。

评论


只要最终用户可以重新链接到LGPL的代码(例如,因为您随软件一起提供了源代码或编译的目标文件),那么静态链接就可以了。此外,如果您的软件仅供内部使用(即仅供组织内部使用,而不是分发的),则可以进行静态链接。这将适用于服务器软件,未分发服务器。

– JBentley
2014年1月10日在1:26



不明白您能给我更多资料(或详细说明)以欣赏您写的内容吗?

–Baskaya
15-10-23在21:27

@Thorn参见LGPL许可条款4.d + e。您或者需要以要求用户进行链接的形式分发,或者分发共享的(动态)库。

– Mark Ransom
2015年10月23日在22:01



#3 楼

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

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

我要补充:(3)链接器可以以更高效的缓存分组方式来安排功能,从而使昂贵的缓存级别未命中率最小化。它还可能特别影响应用程序的启动时间(基于我在Sun C ++编译器中看到的结果)。并且请不要忘记,使用DLL不能执行无效代码消除。取决于语言,DLL代码也可能不是最佳的。虚函数始终是虚的,因为编译器不知道客户端是否会覆盖它。 >
编辑(回答问题,在用户下划线处注明)

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

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

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

所以您将不再有任何好处。

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

评论


我喜欢这个答案,因为它是解决我在问题中提出的观点的唯一答案。

– Eloff
2010年1月5日在20:49

拥有这些DLL技术的参考以及不同操作系统之间的比较将是很有趣的。

– UncleZeiv
2010-4-16 14:39

看起来不错,但是CPU速度绝对没有达到极限。

– Aidiakapi
15年4月15日在20:26

#4 楼

我同意dnmckee提到的要点,此外:静态链接的应用程序可能更易于部署,因为可能会导致更少或没有其他文件依赖项(.dll / .so)。丢失或安装在错误的位置时出现问题。


评论


值得注意的是,来自Google的Go编译器仅会出于此原因而静态编译二进制文件。

–小屋8
2014年4月4日23:53

#5 楼

进行静态链接构建的一个原因是,要验证您对可执行文件具有完全关闭的功能,即,正确解析了所有符号引用。

作为正在构建和测试的大型系统的一部分使用持续集成,使用可执行文件的静态链接版本运行每晚回归测试。有时候,即使动态链接的可执行文件可以成功链接,我们也会看到符号无法解析,并且静态链接也会失败。拼写错误的名称,因此不会静态链接。无论使用深度优先还是宽度优先评估,动态链接程序都不会完全解析所有符号,因此您可以完成一个没有完全关闭的动态链接可执行文件。

评论


非常好的一点,我最近一直在尝试使用我正在使用的一些代码来执行此操作,但是静态编译所有内容令人惊讶地令人讨厌,我只是放弃

– UncleZeiv
2010-4-16 14:35

#6 楼

1 /我去过的项目中,对动态链接与静态链接进行了基准测试,并且确定的差异还不足以切换到动态链接(我不是测试的一部分,我只知道结论)
2 /动态链接通常与PIC(位置无关代码,根据其加载地址无需修改的代码)相关联。根据架构的不同,PIC可能会带来另一个速度下降,但是为了获得在两个可执行文件之间共享动态链接库的好处(如果操作系统使用加载地址的随机性作为安全措施,甚至是同一可执行文件的两个进程)共享,PIC是必需的。我不确定是否所有操作系统都可以将这两个概念分开,但Solaris和Linux可以做到,HP-UX可以做到ISTR。链接到“简易补丁”功能。但是,这种“简单补丁”使小补丁的分发变得容易一些,而复杂补丁的分发成为了噩梦。由于错误的版本是令牌,所以我们常常不得不在一切事情上加全力,并且不得不在客户现场跟踪问题。

我的结论是我使用了静态链接,但是: />
对于诸如动态共享依赖的插件之类的东西来说很重要(共享时很重要(多个进程同时使用的大型库,例如C / C ++运行时,GUI库等),这些库经常被管理

如果要使用“简单补丁”,我认为必须像上面的大库一样对库进行管理:它们必须几乎是独立于已定义的ABI,且不能通过修订对其进行更改。

评论


某些用于非PIC或昂贵PIC处理器的OS将准备动态库以将其加载到内存中的特定地址,如果可以这样做,则它们仅将库的副本映射到与其链接的每个进程。这大大减少了PIC的开销。至少OS X和某些Linux发行版可以做到这一点,我不确定Windows。

–安德鲁·麦格雷戈(Andrew McGregor)
2010年1月3日,11:34

谢谢安德鲁,我不知道某些Linux发行版使用了这个功能。您是否有可以参考的参考文献或可以搜索以了解更多信息的关键词? (FWIW,我听说Windows正在这样做,但是Windows超出了我的能力范围,我无法提及)。

– AProgrammer
2010年1月3日,11:59

我认为您要查找的关键字是“ prelink”-它准备将库快速加载到某个地址,以使程序启动更快。

–布莱布莱德
2013年1月15日13:34



#7 楼

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

评论


+1链接到Drepper的DSO howto,在Linux上制作库的每个人都应该阅读。

– janneb
2010年1月3日,9:58

#8 楼

动态链接的最佳示例是库依赖于所使用的硬件。在远古时代,C数学库被认为是动态的,因此每个平台都可以使用所有处理器功能对其进行优化。

更好的示例可能是OpenGL。 OpenGl是一种由AMD和NVidia以不同方式实现的API。而且由于硬件不同,您将无法在AMD卡上使用NVidia实现。因此,您不能将OpenGL静态链接到程序中。动态链接用于使API针对所有平台进行优化。

#9 楼

在类似Unix的系统上,动态链接可能会使“ root”难以使用在偏僻位置安装了共享库的应用程序。这是因为动态链接程序通常不会注意具有根特权的进程的LD_LIBRARY_PATH或其等效项。有时,静态链接可以节省一天的时间。

或者,安装过程中必须找到这些库,但这会使软件的多个版本难以在计算机上共存。

评论


关于LD_LIBRARY_PATH的要点并不完全是使用共享库的障碍,至少在GNU / Linux中不是。例如。如果将共享库放在相对于程序文件的目录../lib/中,则使用GNU工具链,链接器选项-rpath $ ORIGIN /../ lib将指定从该相对位置搜索库。然后,您可以轻松地将应用程序与所有关联的共享库一起重新定位。使用此技巧,就可以同时拥有多个版本的应用程序和库(假设它们是相关的,如果没有关系,则可以使用符号链接)。

– FooF
2012年6月20日4:07

>用于具有root特权的进程。我认为您是在谈论从非root用户运行的setuid程序-否则,这没有任何意义。 setuid二进制文件在非标准位置的库很奇怪-但由于只有root才能安装这些程序,因此他也可以针对这种情况编辑/etc/ld.so.conf。

–布莱布莱德
2013年1月15日13:40

#10 楼

确实很简单。当您更改源代码时,是否要等待10分钟才能构建代码或20秒?我只能忍受20秒。除此之外,我要么剑拔or张,要么开始考虑如何使用单独的编译和链接将其带回舒适区域。

评论


我实际上尚未对编译速度的差异进行基准测试,但是如果动态链接的速度明显更快,我会进行动态链接。 Boost对我的编译时间做了足够的坏事情。

– Eloff
2010年1月5日在20:45

#11 楼

动态链接需要操作系统花费更多时间来找到动态库并加载它。通过静态链接,所有内容都在一起,并且一次性加载到内存中。在这种情况下,操作系统加载的DLL不是您的应用程序随附的DLL,也不是您的应用程序期望的版本。

评论


重要的是要注意,有许多对策可以避免DLL地狱。

– ocodo
2012年12月26日14:35

#12 楼

尚未讨论的另一个问题是修复库中的错误。

使用静态链接,您不仅必须重建库,还必须重新链接并重新分发可执行文件。如果该库仅在一个可执行文件中使用,则可能不是问题。但是需要重新链接和重新分发的可执行文件越多,痛苦就越大。

通过动态链接,您只需重新构建和重新分发动态库就可以了。

#13 楼

Static linking是将链接内容复制到主二进制文件中并成为单个二进制文件时的编译过程。
缺点:

编译时间较长
Dynamic linking是运行时加载链接内容的进程。该技术允许:

升级链接的二进制文件而无需重新编译可提高ABI稳定性的主二进制文件。缺点:

启动时间较慢(应复制链接的内容)
运行时引发链接器错误


#14 楼

静态链接仅给您一个exe,以便进行更改,您需要重新编译整个程序。而在动态链接中,您只需要对dll进行更改,而在运行exe时,这些更改将在运行时获取。通过动态链接(例如Windows)提供更新和错误修复更加容易。

#15 楼

在越来越多的系统中,极高级别的静态链接可能会对应用程序和系统性能产生巨大的积极影响。

我指的是通常被称为“嵌入式系统”的系统,其中许多现在越来越多地使用通用操作系统,并且这些系统用于所有可想像的事物。我通过构建可引导的i386(32位)系统映像(包括内核和其根文件系统)将NetBSD发挥到了极致,后者包含单个静态链接(通过crunchgen)的二进制文件(带有硬链接)所有本身包含所有标准全功能系统程序(最后只有274个)的程序(除工具链以外的大多数程序),并且其大小小于20 MB(并且可能在只有64MB的系统中非常舒适地运行的内存(即使根文件系统未压缩且完全在RAM中),尽管我无法找到这么小的内存对其进行测试。

前面的文章中已经提到静态链接的二进制文件的启动时间更快(并且可能更快),但这只是图片的一部分,尤其是在将所有目标代码链接到同一文件时,尤其是在操作系统时支持直接从可执行文件中按需分页代码。在这种理想情况下,程序的启动时间实际上可以忽略不计,因为几乎所有代码页都已在内存中并且被外壳程序使用(并且可能正在运行的任何其他后台进程),即使请求的程序具有自引导以来从未运行过,因为可能只需要加载一页内存即可满足程序的运行时要求。

但是,这还不是全部。我通常还通过静态链接所有二进制文件来为完整的开发系统构建和使用NetBSD操作系统安装。即使这需要占用大量磁盘空间(x86_64总共约6.6GB,包括工具链和X11静态链接在内的所有内容)(尤其是如果一个磁盘对所有程序都保留了完整的调试符号表,而另外还有约2.5GB),结果仍然整体运行速度更快,并且某些任务甚至比声称共享库代码页的典型动态链接系统使用更少的内存。磁盘很便宜(甚至是快速磁盘),用于缓存常用磁盘文件的内存也相对便宜,但是CPU周期确实不便宜,并且每次启动都要为每个启动的进程支付init的启动成本, CPU远离需要启动许多进程的任务,特别是当一遍又一遍地使用相同的程序(例如开发系统上的编译器)时,CPU就会停止工作。静态链接的工具链程序可以将我的系统的整个OS多体系结构构建时间减少数小时。我尚未将工具链构建到单个ld.so的二进制文件中,但是我怀疑这样做会节省更多的构建时间,因为这将赢得CPU缓存。

#16 楼

静态链接将程序需要的文件包含在一个可执行文件中。动态链接是您通常认为的那样,它使可执行文件仍然需要DLL,并且位于同一目录中(或DLL可能位于系统文件夹中。 br />

#17 楼

另一个考虑因素是您在库中实际消耗的目标文件(翻译单元)数与可用总数的比较。如果库是由许多目标文件构建的,但是您仅使用其中一些符号,则这可能是支持静态链接的一个论据,因为您仅链接静态链接时使用的对象(通常)并且不t通常会携带未使用的符号。如果使用共享库,则该库包含所有翻译单元,并且可能比您想要或需要的库大得多。