简体   繁体   English

Static 链接与动态链接

[英]Static linking vs dynamic linking

Are there any compelling performance reasons to choose static linking over dynamic linking or vice versa in certain situations?在某些情况下,是否有任何令人信服的性能理由来选择 static 链接而不是动态链接,反之亦然? I've heard or read the following, but I don't know enough on the subject to vouch for its veracity.我听过或读过以下内容,但我对这个主题的了解还不够,无法保证其真实性。

1) The difference in runtime performance between static linking and dynamic linking is usually negligible. 1) static 链接和动态链接之间的运行时性能差异通常可以忽略不计。

2) (1) is not true if using a profiling compiler that uses profile data to optimize program hotpaths because with static linking, the compiler can optimize both your code and the library code. 2) (1) 如果使用使用配置文件数据优化程序热路径的分析编译器则不正确,因为通过 static 链接,编译器可以同时优化您的代码和库代码。 With dynamic linking only your code can be optimized.使用动态链接只能优化您的代码。 If most of the time is spent running library code, this can make a big difference.如果大部分时间都花在运行库代码上,这会产生很大的不同。 Otherwise, (1) still applies.否则,(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 it its presence in most environments.我相信这是促使它在大多数环境中出现的论据。 Here "resources" includes disk space, RAM, and cache space.这里的“资源”包括磁盘空间、RAM 和缓存空间。 Of course, if your dynamic linker is insufficiently flexible there is a risk of DLL hell .当然,如果您的动态链接器不够灵活,则存在DLL 地狱的风险。
  • 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.稍微大一点的小型系统可能很好地支持动态链接,因为它们的内存小到足以使动态链接节省的 RAM 非常有吸引力。 Full blown consumer PCs have, as Mark notes, enormous resources, and you can probably let the convenience issues drive your thinking on this matter.正如马克指出的那样,成熟的消费类 PC 拥有巨大的资源,您可能可以让便利性问题驱动您对此问题的思考。


To address the performance and efficiency issues: it depends .解决性能和效率问题:这取决于.

Classically, dynamic libraries require a some kind of glue layer which often means double dispatch or an extra layer of indirection in function addressing and can cost a little speed (but is function calling time actually a big part of your running time???).传统上,动态库需要某种胶水层,这通常意味着函数寻址中的双重调度或额外的间接层,并且可能会降低速度(但函数调用时间实际上是运行时间的重要组成部分吗???)。

However, if you are running multiple processes which all call the same library a lot, you can end up saving cache lines (and thus winning on running performance) when using dynamic linking relative to using static linking.但是,如果您运行的多个进程都大量调用同一个库,则在使用动态链接相对于使用静态链接时,您最终可以节省缓存行(从而赢得运行性能)。 (Unless modern OS's are smart enough to notice identical segments in statically linked binaries. Seems hard, anyone know?) (除非现代操作系统足够聪明,可以注意到静态链接二进制文件中的相同段。看起来很难,有人知道吗?)

Another issue: loading time.另一个问题:加载时间。 You pay loading costs at some point.您在某个时候支付装载费用。 When you pay this cost depends on how the OS works as well as what linking you use.您何时支付此费用取决于操作系统的工作方式以及您使用的链接。 Maybe you'd rather put off paying it until you know you need it.也许你宁愿推迟支付,直到你知道你需要它。

Note that static-vs-dynamic linking is traditionally not an optimization issue, because they both involve separate compilation down to object files.请注意,静态与动态链接传统上不是优化问题,因为它们都涉及到目标文件的单独编译。 However, this is not required: a compiler can in principle, "compile" "static libraries" to a digested AST form initially, and "link" them by adding those ASTs to the ones generated for the main code, thus empowering global optimization.但是,这不是必需的:原则上,编译器可以最初将“静态库”“编译”为消化后的 AST 形式,然后通过将这些 AST 添加到为主代码生成的 AST 来“链接”它们,从而实现全局优化。 None of the systems I use do this, so I can't comment on how well it works.我使用的系统都没有这样做,所以我无法评论它的工作情况。

The way to answer performance questions is always by testing (and use a test environment as much like the deployment environment as possible).回答性能问题的方法总是通过测试(并尽可能使用与部署环境相似的测试环境)。

1) is based on the fact that calling a DLL function is always using an extra indirect jump. 1) 基于这样一个事实,即调用 DLL 函数总是使用额外的间接跳转。 Today, this is usually negligible.今天,这通常可以忽略不计。 Inside the DLL there is some more overhead on i386 CPU's, because they can't generate position independent code.在 DLL 内部,i386 CPU 的开销更大,因为它们无法生成位置无关代码。 On amd64, jumps can be relative to the program counter, so this is a huge improvement.在 amd64 上,跳转可以相对于程序计数器,所以这是一个巨大的改进。

2) This is correct. 2)这是正确的。 With optimizations guided by profiling you can usually win about 10-15 percent performance.通过分析指导的优化,您通常可以获得大约 10-15% 的性能。 Now that CPU speed has reached its limits it might be worth doing it.现在 CPU 速度已达到极限,可能值得这样做。

I would add: (3) the linker can arrange functions in a more cache efficient grouping, so that expensive cache level misses are minimised.我要补充的是:(3) 链接器可以将函数安排在一个更高效的缓存分组中,从而最大限度地减少昂贵的缓存级别未命中。 It also might especially effect the startup time of applications (based on results i have seen with the Sun C++ compiler)它也可能特别影响应用程序的启动时间(基于我使用 Sun C++ 编译器看到的结果)

And don't forget that with DLLs no dead code elimination can be performed.并且不要忘记,使用 DLL 无法消除死代码。 Depending on the language, the DLL code might not be optimal either.根据语言的不同,DLL 代码也可能不是最佳的。 Virtual functions are always virtual because the compiler doesn't know whether a client is overwriting it.虚函数始终是虚函数,因为编译器不知道客户端是否正在覆盖它。

For these reasons, in case there is no real need for DLLs, then just use static compilation.由于这些原因,如果真的不需要 DLL,那么只需使用静态编译。

EDIT (to answer the comment, by user underscore)编辑(回答评论,由用户下划线)

Here is a good resource about the position independent code problem http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/这是一个关于位置无关代码问题的好资源http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

As explained x86 does not have them AFAIK for anything else then 15 bit jump ranges and not for unconditional jumps and calls.正如所解释的,x86 没有 AFAIK 用于其他任何东西,然后是 15 位跳转范围,而不是用于无条件跳转和调用。 That's why functions (from generators) having more then 32K have always been a problem and needed embedded trampolines.这就是为什么具有超过 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).但是在像 Linux 这样流行的 x86 操作系统上,您不需要关心 .so/DLL 文件是否不是使用gcc开关-fpic (强制使用间接跳转表)生成的。 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.但是在这样做时,它会使代码段不可共享,并且需要将代码从磁盘完整映射到内存并在使用之前将其全部接触(清空大部分缓存,命中 TLB)等。 曾经有一段时间当这被认为是缓慢的。

So you would not have any benefit anymore.这样你就不会再有任何好处了。

I do not recall what OS (Solaris or FreeBSD) gave me problems with my Unix build system because I just wasn't doing this and wondered why it crashed until I applied -fPIC to gcc .我不记得是什么操作系统(Solaris 或 FreeBSD)给我的 Unix 构建系统带来了问题,因为我只是没有这样做,并且想知道为什么在我将-fPIC应用于gcc之前它崩溃了。

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

I agree with the points dnmckee mentions, plus:我同意 dnmckee 提到的观点,另外:

  • Statically linked applications might be easier to deploy, since there are fewer or no additional file dependencies (.dll / .so) that might cause problems when they're missing or installed in the wrong place.静态链接的应用程序可能更容易部署,因为当它们丢失或安装在错误的位置时可能会导致问题的附加文件依赖项(.dll / .so)较少或没有。

One reason to do a statically linked build is to verify that you have full closure for the executable, ie that all symbol references are resolved correctly.进行静态链接构建的原因之一是验证您对可执行文件已完全关闭,即所有符号引用都已正确解析。

As a part of a large system that was being built and tested using continuous integration, the nightly regression tests were run using a statically linked version of the executables.作为使用持续集成构建和测试的大型系统的一部分,夜间回归测试使用可执行文件的静态链接版本运行。 Occasionally, we would see that a symbol would not resolve and the static link would fail even though the dynamically linked executable would link successfully.有时,我们会看到符号无法解析并且静态链接将失败,即使动态链接的可执行文件链接成功。

This was usually occurring when symbols that were deep seated within the shared libs had a misspelt name and so would not statically link.这通常发生在位于共享库中的符号的名称拼写错误时,因此不会静态链接。 The dynamic linker does not completely resolve all symbols, irrespective of using depth-first or breadth-first evaluation, so you can finish up with a dynamically linked executable that does not have full closure.无论使用深度优先还是广度优先评估,动态链接器都不会完全解析所有符号,因此您可以完成一个没有完全闭包的动态链接可执行文件。

1/ I've been on projects where dynamic linking vs static linking was benchmarked and the difference wasn't determined small enough to switch to dynamic linking (I wasn't part of the test, I just know the conclusion) 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). 2/ 动态链接通常与PIC(Position Independent Code,不需要根据加载地址进行修改的代码)相关联。 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).根据架构,PIC 可能会带来另一次减速,但为了获得在两个可执行文件之间共享动态链接库的好处(如果操作系统使用加载地址随机化作为安全措施,甚至同一可执行文件的两个进程),则需要这样做。 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.我不确定所有操作系统都允许将这两个概念分开,但 Solaris 和 Linux 这样做,而 HP-UX 也这样做。

3/ I've been on other projects which used dynamic linking for the "easy patch" feature. 3/ 我一直在其他项目中使用动态链接来实现“简单补丁”功能。 But this "easy patch" makes the distribution of small fix a little easier and of complicated one a versioning nightmare.但是这个“简单的补丁”使小补丁的分发变得更容易一些,而复杂的补丁则是版本控制的噩梦。 We often ended up by having to push everything plus having to track problems at customer site because the wrong version was token.我们经常最终不得不推送所有内容,并且不得不在客户站点跟踪问题,因为错误的版本是令牌。

My conclusion is that I'd used static linking excepted:我的结论是,我使用了静态链接除外:

  • for things like plugins which depend on dynamic linking对于依赖动态链接的插件之类的东西

  • when sharing is important (big libraries used by multiple processes at the same time like C/C++ runtime, GUI libraries, ... which often are managed independently and for which the ABI is strictly defined)当共享很重要时(多个进程同时使用的大型库,如 C/C++ 运行时、GUI 库……它们通常是独立管理的,并且 ABI 是严格定义的)

If one want to use the "easy patch", I'd argue that the libraries have to be managed like the big libraries above: they must be nearly independent with a defined ABI that must not to be changed by fixes.如果有人想使用“简单补丁”,我认为必须像上面的大库一样管理库:它们必须几乎独立于定义的 ABI,并且不能被修复程序更改。

这将详细讨论 linux 上的共享库和性能影响。

It is pretty simple, really.这很简单,真的。 When you make a change in your source code, do you want to wait 10 minutes for it to build or 20 seconds?当您对源代码进行更改时,您希望等待 10 分钟来构建它还是等待 20 秒? Twenty seconds is all I can put up with.二十秒是我所能忍受的。 Beyond that, I either get out the sword or start thinking about how I can use separate compilation and linking to bring it back into the comfort zone.除此之外,我要么放弃剑,要么开始考虑如何使用单独的编译和链接将其带回舒适区。

Best example for dynamic linking is, when the library is dependent on the used hardware.动态链接的最佳示例是,当库依赖于使用的硬件时。 In ancient times the C math library was decided to be dynamic, so that each platform can use all processor capabilities to optimize it.在古代,C 数学库被决定为动态的,以便每个平台都可以使用所有处理器功能来优化它。

An even better example might be OpenGL.一个更好的例子可能是 OpenGL。 OpenGl is an API that is implemented differently by AMD and NVidia. OpenGl 是一种由 AMD 和 NVidia 以不同方式实现的 API。 And you are not able to use an NVidia implementation on an AMD card, because the hardware is different.而且您不能在 AMD 卡上使用 NVidia 实现,因为硬件不同。 You cannot link OpenGL statically into your program, because of that.因此,您不能将 OpenGL 静态链接到您的程序中。 Dynamic linking is used here to let the API be optimized for all platforms.这里使用动态链接让 API 针对所有平台进行优化。

On Unix-like systems, dynamic linking can make life difficult for 'root' to use an application with the shared libraries installed in out-of-the-way locations.在类 Unix 系统上,动态链接会使“root”难以使用共享库安装在偏远位置的应用程序。 This is because the dynamic linker generally won't pay attention to LD_LIBRARY_PATH or its equivalent for processes with root privileges.这是因为动态链接器通常不会注意 LD_LIBRARY_PATH 或其对具有 root 权限的进程的等效项。 Sometimes, then, static linking saves the day.有时,静态链接可以节省一天的时间。

Alternatively, the installation process has to locate the libraries, but that can make it difficult for multiple versions of the software to coexist on the machine.或者,安装过程必须定位库,但这会使多个版本的软件难以在机器上共存。

Static linking is a process in compile time when a linked content is copied into the primary binary and becomes a single binary. Static linking是编译时将链接内容复制到主二进制文件并成为单个二进制文件的过程。

Cons:缺点:

  • compile time is longer编译时间更长
  • output binary is bigger输出二进制更大

Dynamic linking is a process in runtime when a linked content is loaded. Dynamic linking是加载链接内容时运行时的一个过程。 This technic allows to:该技术允许:

  • upgrade linked binary without recompiling a primary one that increase an ABI stability [About]无需重新编译主要二进制文件即可升级链接二进制文件以提高ABI稳定性[关于]
  • has a single shared copy有一个共享副本

Cons:缺点:

  • start time is slower(linked content should be copied)开始时间较慢(应复制链接内容)
  • linker errors are thrown in runtime在运行时抛出链接器错误

[iOS Static vs Dynamic framework] 【iOS静态vs动态框架】

Dynamic linking requires extra time for the OS to find the dynamic library and load it.动态链接需要额外的时间让操作系统找到动态库并加载它。 With static linking, everything is together and it is a one-shot load into memory.使用静态链接,一切都在一起,它是一次性加载到内存中。

Also, see DLL Hell .另请参阅DLL 地狱 This is the scenario where the DLL that the OS loads is not the one that came with your application, or the version that your application expects.在这种情况下,操作系统加载的 DLL 不是应用程序附带的 DLL,也不是应用程序期望的版本。

Another issue not yet discussed is fixing bugs in the library.另一个尚未讨论的问题是修复库中的错误。

With static linking, you not only have to rebuild the library, but will have to relink and redestribute the executable.使用静态链接,您不仅需要重建库,还必须重新链接和重新分发可执行文件。 If the library is just used in one executable, this may not be an issue.如果该库仅在一个可执行文件中使用,则这可能不是问题。 But the more executables that need to be relinked and redistributed, the bigger the pain is.但是需要重新链接和重新分发的可执行文件越多,痛苦就越大。

With dynamic linking, you just rebuild and redistribute the dynamic library and you are done.使用动态链接,您只需重建和重新分发动态库即可。

Static linking includes the files that the program needs in a single executable file.静态链接将程序需要的文件包含在单个可执行文件中。

Dynamic linking is what you would consider the usual, it makes an executable that still requires DLLs and such to be in the same directory (or the DLLs could be in the system folder).动态链接是您通常认为的,它使可执行文件仍然需要 DLL 并且位于同一目录中(或者 DLL 可能位于系统文件夹中)。

(DLL = dynamic link library) (DLL =动态链接库)

Dynamically linked executables are compiled faster and aren't as resource-heavy.动态链接的可执行文件编译速度更快,并且不会占用大量资源。

static linking gives you only a single exe, inorder to make a change you need to recompile your whole program.静态链接只为您提供一个 exe,为了进行更改,您需要重新编译整个程序。 Whereas in dynamic linking you need to make change only to the dll and when you run your exe, the changes would be picked up at runtime.Its easier to provide updates and bug fixes by dynamic linking (eg: windows).而在动态链接中,您只需要对 dll 进行更改,当您运行 exe 时,将在运行时获取更改。通过动态链接(例如:Windows)更容易提供更新和错误修复。

There are a vast and increasing number of systems where an extreme level of static linking can have an enormous positive impact on applications and system performance.有大量且越来越多的系统,其中极端级别的静态链接可以对应用程序和系统性能产生巨大的积极影响。

I refer to what are often called "embedded systems", many of which are now increasingly using general-purpose operating systems, and these systems are used for everything imaginable.我指的是通常称为“嵌入式系统”的东西,其中许多现在越来越多地使用通用操作系统,而这些系统用于所有可以想象的事情。

An extremely common example are devices using GNU/Linux systems using Busybox .一个非常常见的例子是使用 GNU/Linux 系统的设备使用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 mega bytes 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).我通过构建一个包含内核和根文件系统的可引导 i386(32 位)系统映像,在NetBSD 中将这一点发挥到极致,后者包含一个带有硬链接的静态链接(通过crunchgen )二进制文件所有程序本身包含所有(最后是 274 个)标准的全功能系统程序(除了工具链之外的大多数),并且它的大小小于 20字节(并且可能在只有64MB 的内存(即使根文件系统未压缩且完全在 RAM 中),但我一直无法找到这么小的内存来测试它)。

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.在这种理想的情况下程序的启动时间是从字面上可以忽略不计,因为代码几乎所有页面都已经在内存中,并在使用由外壳(与和init可能正在运行任何其他后台进程),即使所请求的程序有自启动以来从未运行过,因为可能只需要加载一页内存来满足程序的运行时要求。

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.我还通常通过静态链接所有二进制文件为我的完整开发系统构建和使用 NetBSD 操作系统安装。 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.即使这需要大量更多的磁盘空间(x86_64 总共约 6.6GB,包括工具链和 X11 静态链接)(特别是如果一个完整的调试符号表可用于所有程序,另外约 2.5GB),结果仍然整体运行速度更快,对于某些任务,甚至比典型的旨在共享库代码页的动态链接系统使用更少的内存。 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.磁盘是便宜(甚至快速的磁盘),以及内存来缓存频繁使用的磁盘文件也相对便宜,但CPU周期真的不是,并支付了ld.so每个进程启动每次启动需要几个小时的时间启动成本和几个小时的 CPU 周期远离需要启动多个进程的任务,特别是当重复使用相同的程序时,例如开发系统上的编译器。 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.我还没有将工具链构建到我的单个crunchgen ed 二进制文件中,但我怀疑当我这样做时,由于 CPU 缓存的胜利,会节省更多的构建时间。

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.如果一个库是由许多目标文件构建的,但你只使用其中几个的符号,这可能是支持静态链接的一个论据,因为你只链接你在静态链接时使用的对象(通常)而不是“ t 通常携带未使用的符号。 If you go with a shared lib, that lib contains all translation units and could be much larger than what you want or need.如果您使用共享库,则该库包含所有翻译单元,并且可能比您想要或需要的大得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM