在某些情况下,选择静态链接而不是动态链接或反之,是否有令人信服的性能原因?我听过或读过以下内容,但我对这个主题了解不够,无法保证其真实性。

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

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


Dynamic linking can reduce total resource consumption (if more than one process shares the same library (including the version in "the same", of course)). I believe this is the argument that drives its presence in most environments. Here "resources" include disk space, RAM, and cache space. Of course, if your dynamic linker is insufficiently flexible there is a risk of DLL hell. Dynamic linking means that bug fixes and upgrades to libraries propagate to improve your product without requiring you to ship anything. Plugins always call for dynamic linking. Static linking, means that you can know the code will run in very limited environments (early in the boot process, or in rescue mode). Static linking can make binaries easier to distribute to diverse user environments (at the cost of sending a larger and more resource-hungry program). Static linking may allow slightly faster startup times, but this depends to some degree on both the size and complexity of your program and on the details of the OS's loading strategy.


Some edits to include the very relevant suggestions in the comments and in other answers. I'd like to note that the way you break on this depends a lot on what environment you plan to run in. Minimal embedded systems may not have enough resources to support dynamic linking. Slightly larger small systems may well support dynamic linking because their memory is small enough to make the RAM savings from dynamic linking very attractive. Full-blown consumer PCs have, as Mark notes, enormous resources, and you can probably let the convenience issues drive your thinking on this matter.


要解决性能和效率问题:视情况而定。

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

但是,如果您正在运行多个进程,并且它们都经常调用同一个库,那么使用动态链接而不是静态链接时,您最终可以节省缓存行(从而赢得运行性能)。(除非现代操作系统足够智能,能够在静态链接的二进制文件中注意到相同的段。似乎很难,有人知道吗?)

另一个问题:加载时间。你需要支付装货费用。你什么时候支付这个费用取决于操作系统的工作方式以及你使用的链接。也许你宁愿推迟支付,直到你知道你需要它。

注意,静态链接与动态链接传统上不是一个优化问题,因为它们都涉及到对目标文件的单独编译。然而,这并不是必需的:原则上,编译器可以最初将“静态库”“编译”到一个摘要的AST表单中,并通过将这些AST添加到为主代码生成的AST中来“链接”它们,从而实现全局优化。我使用的系统都没有做到这一点,所以我不能评论它的工作效果如何。

回答性能问题的方法总是通过测试(并尽可能使用与部署环境相似的测试环境)。


我同意dnmckee提到的观点,另外:

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


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一直是一个问题,需要嵌入蹦床。

But on popular x86 OS like Linux you do not need to care if the .so/DLL file is not generated with the gcc switch -fpic (which enforces the use of the indirect jump tables). Because if you don't, the code is just fixed like a normal linker would relocate it. But while doing this it makes the code segment non shareable and it would need a full mapping of the code from disk into memory and touching it all before it can be used (emptying most of the caches, hitting TLBs) etc. There was a time when this was considered slow.

这样你就没有任何收益了。

我不记得什么操作系统(Solaris或FreeBSD)给我的Unix构建系统的问题,因为我只是没有这样做,不知道为什么它崩溃,直到我应用-fPIC到gcc。


执行静态链接构建的一个原因是验证可执行文件是否完全关闭,即所有符号引用都正确解析。

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

这种情况通常发生在共享库中位置较深的符号有拼写错误的名称,因此不能进行静态链接时。无论使用深度优先还是宽度优先求值,动态链接器都不能完全解析所有符号,因此您可以完成一个没有完全闭包的动态链接可执行文件。


动态链接需要额外的时间让操作系统找到动态库并加载它。使用静态链接,所有内容都在一起,并且只需要一次性加载到内存中。

另外,参见DLL地狱。在这种情况下,操作系统加载的DLL不是应用程序自带的,也不是应用程序期望的版本。


这真的很简单。当您在源代码中进行更改时,您希望等待10分钟或20秒来构建它?我只能忍受20秒。除此之外,我要么拿出剑来,要么开始考虑如何使用单独的编译和链接将其带回舒适区。


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


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

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

使用动态链接,只需重新构建和重新分发动态库,就完成了工作。


1/我曾经参与过一个项目,其中动态链接和静态链接是基准测试,差异并没有小到可以切换到动态链接(我没有参与测试,我只知道结论)

2/ Dynamic linking is often associated with PIC (Position Independent Code, code which doesn't need to be modified depending on the address at which it is loaded). Depending on the architecture PIC may bring another slowdown but is needed in order to get benefit of sharing a dynamically linked library between two executable (and even two process of the same executable if the OS use randomization of load address as a security measure). I'm not sure that all OS allow to separate the two concepts, but Solaris and Linux do and ISTR that HP-UX does as well.

3/我在其他项目中使用了动态链接的“简单补丁”功能。但是这个“简单补丁”使得小补丁的发布更加容易,而复杂补丁的发布则成为版本控制的噩梦。因为错误的版本是token,我们经常不得不推送所有内容,并在客户站点跟踪问题。

我的结论是,我使用静态链接除外:

比如依赖于动态链接的插件 当共享是重要的(大型库被多个进程同时使用,如C/ c++运行时,GUI库,…它们通常是独立管理的,ABI对此有严格的定义)

如果有人想使用“简单的补丁”,我认为库必须像上面的大库一样管理:它们必须几乎独立,具有定义好的ABI,不能被修复更改。


在类unix系统上,动态链接会使“root”很难使用安装在偏僻位置的共享库的应用程序。这是因为对于具有根权限的进程,动态连接器通常不会注意LD_LIBRARY_PATH或其等效内容。有时,静态链接可以解决问题。

另外,安装过程必须确定库的位置,但这可能会使计算机上的多个软件版本难以共存。


动态链接的最佳示例是库依赖于所使用的硬件。在古代,C数学库是动态的,这样每个平台都可以使用所有的处理器功能来优化它。

一个更好的例子可能是OpenGL。OpenGl是一个由AMD和NVidia实现的不同的API。你不能在AMD卡上使用NVidia实现,因为硬件是不同的。因此,你不能静态地将OpenGL链接到你的程序中。这里使用了动态链接,以便为所有平台优化API。


静态链接只给你一个单一的exe,为了做出改变,你需要重新编译你的整个程序。而在动态链接中,您只需要对dll进行更改,当您运行exe时,这些更改将在运行时被拾取。通过动态链接(例如:windows)更容易提供更新和错误修复。


在大量且越来越多的系统中,极端的静态链接可以对应用程序和系统性能产生巨大的积极影响。

我指的是通常所说的“嵌入式系统”,其中许多现在越来越多地使用通用操作系统,这些系统被用于所有可以想象到的事情。

An extremely common example are devices using GNU/Linux systems using Busybox. I've taken this to the extreme with NetBSD by building a bootable i386 (32-bit) system image that includes both a kernel and its root filesystem, the latter which contains a single static-linked (by crunchgen) binary with hard-links to all programs that itself contains all (well at last count 274) of the standard full-feature system programs (most except the toolchain), and it is less than 20 megabytes in size (and probably runs very comfortably in a system with only 64MB of memory (even with the root filesystem uncompressed and entirely in RAM), though I've been unable to find one so small to test it on).

It has been mentioned in earlier posts that the start-up time of a static-linked binaries is faster (and it can be a lot faster), but that is only part of the picture, especially when all object code is linked into the same file, and even more especially when the operating system supports demand paging of code direct from the executable file. In this ideal scenario the startup time of programs is literally negligible since almost all pages of code will already be in memory and be in use by the shell (and and init any other background processes that might be running), even if the requested program has not ever been run since boot since perhaps only one page of memory need be loaded to fulfill the runtime requirements of the program.

However that's still not the whole story. I also usually build and use the NetBSD operating system installs for my full development systems by static-linking all binaries. Even though this takes a tremendous amount more disk space (~6.6GB total for x86_64 with everything, including toolchain and X11 static-linked) (especially if one keeps full debug symbol tables available for all programs another ~2.5GB), the result still runs faster overall, and for some tasks even uses less memory than a typical dynamic-linked system that purports to share library code pages. Disk is cheap (even fast disk), and memory to cache frequently used disk files is also relatively cheap, but CPU cycles really are not, and paying the ld.so startup cost for every process that starts every time it starts will take hours and hours of CPU cycles away from tasks which require starting many processes, especially when the same programs are used over and over, such as compilers on a development system. Static-linked toolchain programs can reduce whole-OS multi-architecture build times for my systems by hours. I have yet to build the toolchain into my single crunchgen'ed binary, but I suspect when I do there will be more hours of build time saved because of the win for the CPU cache.


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

动态链接是你通常会考虑的,它使一个可执行文件仍然需要dll等在同一个目录(或者dll可以在系统文件夹中)。

(动态链接库)

动态链接的可执行文件编译速度更快,资源也不那么繁重。


静态链接是在编译时将链接的内容复制到主二进制文件中并变成单个二进制文件的过程。

缺点:

编译时间更长 输出二进制更大

动态链接是在运行时加载链接内容的过程。这项技术允许:

升级链接二进制文件而不重新编译主二进制文件,增加ABI稳定性[关于] 是否有单个共享副本

缺点:

开始时间较慢(应复制链接内容) 链接器错误在运行时抛出

[iOS静态vs动态框架]


Another consideration is the number of object files (translation units) that you actually consume in a library vs the total number available. If a library is built from many object files, but you only use symbols from a few of them, this might be an argument for favoring static linking, since you only link the objects that you use when you static link (typically) and don't normally carry the unused symbols. If you go with a shared lib, that lib contains all translation units and could be much larger than what you want or need.