静态链接与动态链接

491
在某些情况下,是否有充分的性能原因选择静态链接或动态链接?我听到或读到了以下内容,但我对此并不了解足够的知识来证明其真实性。
1)通常情况下,静态链接和动态链接之间的运行时性能差异微不足道。
2)如果使用利用档案数据来优化程序热点路径的 profiling 编译器,(1)不成立,因为在静态链接中,编译器可以优化您的代码和库代码。而使用动态链接仅可以优化您的代码。如果大部分时间花费在运行库代码上,这可能会产生很大的影响。否则,(1)仍然适用。

77
使用静态链接时,编译器可以优化库代码,但前提是编译器也要编译库代码!如果您只连接到预编译的目标文件,编译器将无法对其进行优化。 - Roger Pate
3
如果那是真的,那么你是正确的,但现代编译器是否完全正确还存在疑问,如果有人能够验证这一点,那就太好了。 - Eloff
5
大多数C/C++编译器将代码编译为本机代码,无法进行进一步的代码优化。如果将代码编译为某种中间语言(如.Net IL),则当库加载时会调用JIT编译器将其编译为本机代码。随着JIT编译器的不断演化,最终编译结果可以越来越好。 - Tarydon
3
如果启用LTCG,VS2008可以完全做到这一点。(虽然库文件会变得非常大..)我曾经尝试过它,对于那些对“我的编译器能为我做什么”感兴趣的人来说,它简直是神奇。 - peterchen
16个回答

9
在类Unix系统上,动态链接可能会给'root'在使用安装在偏僻位置的共享库的应用程序时带来困难。这是因为动态链接器通常不会对具有根特权的进程关注LD_LIBRARY_PATH或其等效项。有时候,静态链接可以拯救今天。

另一种选择是安装过程必须定位库,但这可能会使多个软件版本在机器上共存变得困难。


1
关于LD_LIBRARY_PATH的问题并不是在GNU/Linux中使用共享库的障碍,至少不是完全如此。例如,如果您将共享库放在相对于程序文件的目录../lib/中,那么使用GNU工具链的链接器选项-rpath $ORIGIN/../lib将指定从该相对位置搜索库。然后,您可以轻松地将应用程序与所有相关的共享库一起重新定位。使用这个技巧,即使有多个版本的应用程序和库也没有问题(假设它们是相关的,如果不是,您可以使用符号链接)。 - FooF
针对具有根权限的进程。 我认为你在谈论从非根用户运行的setuid程序 - 否则这没有意义。而带有非标准位置库的setuid二进制文件很奇怪 - 但由于只有root可以安装这些程序,他也可以编辑/etc/ld.so.conf以解决这个问题。 - Blaisorblade

7

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

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

使用动态链接,您只需重新构建和重新分发动态库即可完成。


6

静态链接包含程序需要的文件,放在单个可执行文件中。

动态链接是通常使用的方式,它生成一个仍需要DLL等文件在同一目录中的可执行文件(或者DLL可以在系统文件夹中)。

(DLL = 动态链接库)

动态链接的可执行文件编译速度更快,资源占用更少。


4

有许多系统,静态链接的极致水平对应用程序和系统性能有巨大的积极影响。

我所说的通常被称为“嵌入式系统”,其中许多现在越来越使用通用操作系统,并且这些系统用于各种用途。

一个非常常见的例子是使用Busybox的GNU/Linux系统设备。我通过使用crunchgen进行静态链接,在NetBSD上构建了一个可引导的i386(32位)系统映像,其中包括内核和根文件系统,后者包含单个静态链接二进制文件,并且硬链接到所有程序,它本身包含所有(至少274个)标准全功能系统程序(除了工具链),大小不到20字节(即使根文件系统未压缩且完全在RAM中运行,也可能在仅有64MB内存的系统中非常舒适地运行,尽管我无法找到一个如此小的系统来测试它)。

早些时候的帖子中提到了静态链接二进制文件的启动时间更快(而且可能快得多),但这只是其中的一部分,特别是当所有目标代码都链接到同一个文件中时,更是如此,尤其是当操作系统支持直接从可执行文件进行代码需求分页时。在这种理想的情况下,程序的启动时间几乎可以被忽略,因为几乎所有代码页面已经在内存中,并被shell(和任何其他后台进程,例如init)使用,即使所请求的程序自引导以来从未运行过,因为也许只需要加载一页内存即可满足程序的运行时要求。
然而,这还不是全部。我通常通过静态链接所有二进制文件来构建和使用NetBSD操作系统安装包作为我的完整开发系统。尽管这需要更多的磁盘空间(对于包括工具链和X11静态链接在内的x86_64总共约6.6GB,特别是如果为所有程序保留完整的调试符号表,另外约2.5GB),但结果总体上运行速度更快,并且对于某些任务甚至使用的内存比典型的声称共享库代码页的动态链接系统少。磁盘便宜(即使是快速磁盘),用于缓存频繁使用的磁盘文件的内存也相对便宜,但CPU周期确实不便宜,并且为每个启动的过程付出ld.so启动成本会从需要启动许多进程的任务中花费数小时的CPU周期,特别是当相同的程序反复使用时,例如在开发系统上的编译器。静态链接的工具链程序可以将我的系统的整个OS多架构构建时间减少数小时。我还没有将工具链构建到我的单个crunchgen二进制文件中,但我认为当我这样做时,由于CPU缓存的胜利,将节省更多的构建时间。

3

静态链接只会给你一个单一的可执行文件,如果要进行更改,需要重新编译整个程序。而动态链接仅需要对dll进行更改,当您运行可执行文件时,更改将在运行时被捕获。通过动态链接更容易提供更新和错误修复(例如:Windows)。


0
另一个考虑因素是实际在库中使用的目标文件(翻译单元)数量与可用总数之间的差异。如果一个库是由许多目标文件构建的,但您只使用其中几个文件中的符号,则这可能是支持静态链接的理由,因为当您进行静态链接时,您只链接使用的对象(通常),并且通常不携带未使用的符号。如果您选择共享库,则该库包含所有翻译单元,并且可能比您想要或需要的要大得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接