首页 文章

静态链接与动态链接

提问于
浏览
356

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

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

2)(1)如果使用使用配置文件数据优化程序热路径的配置文件编译器,则不成立,因为通过静态链接,编译器可以优化代码和库代码 . 通过动态链接,只需优化您的代码 . 如果大部分时间都花在运行库代码上,那么这可能会产生很大的不同 . 否则,(1)仍然适用 .

15 回答

  • 1

    静态链接包括程序在单个可执行文件中需要的文件 .

    动态链接是您通常会考虑的,它会生成一个仍然需要DLL的可执行文件,并且它们位于同一目录中(或者DLL可能位于系统文件夹中) .

    (DLL = dynamic link 库)

    动态链接的可执行文件编译得更快,并且不会占用大量资源 .

  • 31
    • Dynamic 链接可以 reduce total resource consumption (如果多个进程共享同一个库(当然包括"the same"中的版本)) . 我相信这是推动它在大多数环境中存在的论点 . 这里"resources"包括磁盘空间,RAM和缓存空间 . 当然,如果您的动态链接器不够灵活,则存在DLL hell的风险 .

    • Dynamic 链接意味着错误修复和升级到库 propagate 以改进您的产品,而无需您运送任何东西 .

    • Plugins 始终要求 dynamic 链接 .

    • Static 链接,意味着您可以知道代码将在非常 limited environments (在引导过程的早期或在救援模式下)运行 .

    • Static 链接可以使二进制文件 easier to distribute 到不同的用户环境(以发送大量和更多资源饥饿程序为代价) .

    • Static 链接可能会略微允许 faster startup 次,但这在某种程度上取决于程序的大小和复杂程度以及操作系统加载策略的详细信息 .


    一些编辑在评论和其他答案中包含非常相关的建议 . 我想指出,你打破这种方式的方式很大程度上取决于你计划运行的环境 . 最小的嵌入式系统可能没有足够的资源来支持动态链接 . 略大的小型系统可能很好地支持链接,因为它们的内存足够小,可以使动态链接节省的RAM非常有吸引力 . 正如马克所指出的那样,全面的消费者PC拥有巨大的资源,你可能会让便利问题引发你对这件事的思考 .


    解决性能和效率问题: it depends .

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

    但是,如果您正在运行多个进程,这些进程都会大量调用同一个库,那么在使用静态链接相对使用动态链接时,最终可以保存缓存行(从而赢得运行性能) . (除非现代操作系统足够智能,能够注意到静态链接二进制文件中的相同段 . 看起来很难,有人都知道吗?)

    另一个问题:加载时间 . 您在某个时候支付装载成本 . 支付此费用取决于操作系统的工作方式以及您使用的链接 . 也许你宁愿推迟支付,直到你知道你需要它为止 .

    请注意,静态与动态链接传统上不是优化问题,因为它们都涉及单独编译到目标文件 . 但是,这不是必需的:编译器原则上可以初始化为消化的AST形式,并且通过将这些AST添加到为主代码生成的AST来实现它们,从而赋予全局优化能力 . 我使用的系统都没有这样做,所以我无法评论它的工作情况 .

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

  • 19

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

  • 21

    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没有AFAIK用于其他任何东西,然后是15位跳转范围而不是无条件跳转和调用 . 这就是为什么具有超过32K的功能(来自发电机)一直是一个问题,需要嵌入式蹦床 .

    但是在像Linux这样流行的x86操作系统上,您根本不需要关心是否使用 gcc 开关 -fpic (强制使用间接跳转表)生成SO / DLL文件 . 因为如果你没有'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 TLB' s)等 . 有一段时间这被认为是缓慢的......太慢了 .

    所以你再也没有任何好处了 .

    我不这样做,并想知道为什么它崩溃直到我将 -fPIC 应用于 gcc .

  • 10

    我同意dnmckee提到的观点,加上:

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

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

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

    这通常发生在共享库中深处的符号具有拼写错误的名称,因此不会静态链接 . 无论使用深度优先或广度优先评估,动态链接器都不会完全解析所有符号,因此您可以使用没有完全关闭的动态链接可执行文件 .

  • 10

    1 /我一直在进行动态链接与静态链接进行基准测试的项目,并且差异不足以切换到动态链接(我不是测试的一部分,我只知道结论)

    2 /动态链接通常与PIC相关联(位置无关代码,不需要根据加载的地址修改代码) . 取决于体系结构,PIC可能带来另一个减速但是为了获得在两个可执行文件之间共享动态链接库的好处(如果OS使用加载地址的随机化作为安全措施,则甚至是相同可执行文件的两个进程) . 我不确定所有操作系统是否允许将这两个概念分开,但是Solaris和Linux确实可以和HP-UX一样 .

    3 /我一直在使用动态链接进行“简单补丁”功能的其他项目 . 但是这个“简单的补丁”使得小修补程序的分发变得更容易,并且复杂的版本化噩梦 . 我们经常因为错误的版本是令牌而必须推送所有内容以及必须跟踪客户站点上的问题 .

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

    • 用于依赖动态链接的插件之类的东西

    • 当共享很重要时(多个进程同时使用的大型库,如C / C运行时,GUI库,......通常是独立管理的,并且严格定义了ABI)

    如果想要使用“简单补丁”,我认为库必须像上面的大型库一样进行管理:它们必须几乎独立于定义的ABI,不能通过修复来改变 .

  • 7

    This详细讨论了Linux上的共享库和性能冲击 .

  • 42

    在类Unix系统上,动态链接可能使“root”生活变得困难,因为在偏远的位置安装了共享库的应用程序 . 这是因为动态链接器通常不会关注具有root权限的进程的LD_LIBRARY_PATH或其等价物 . 有时候,静态链接可以节省一天的时间 .

    或者,安装过程必须定位库,但这可能使多个版本的软件难以在机器上共存 .

  • 5

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

  • 7

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

    另请参阅DLL Hell . 这是操作系统加载的DLL不是的情况随应用程序提供的应用程序或应用程序所需的版本 .

  • 3

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

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

  • 64

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

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

    通过动态链接,您只需重建和重新分发动态库即可完成 .

  • 2

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

  • 315

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

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

    一个非常常见的例子是使用Busybox的GNU / Linux系统的设备 . 通过构建一个可启动的i386(32位)系统映像,包括内核及其根文件系统,后者包含一个带有硬链接的单个静态链接(通过 crunchgen )二进制文件系统,我已经通过NetBSD实现了这一目标 . 所有程序本身包含标准全功能系统程序的 all (最后计数为274)(大多数除了工具链),并且它的大小小于20 mega 字节(并且可能在仅有系统的情况下非常舒适地运行64MB内存(即使根文件系统未压缩且完全在RAM中),但我一直无法找到一个如此小的内存来测试它 .

    在之前的帖子中已经提到静态链接二进制文件的启动时间更快(并且它可以快得多),但这只是图片的一部分,特别是当所有目标代码链接到相同的时候文件,甚至更特别是当操作系统支持从可执行文件直接请求代码分页时 . 在这种理想的情况下,程序的启动时间实际上可以忽略不计,因为几乎所有代码页都已经在内存中并且被shell使用(以及 init 可能正在运行的任何其他后台进程),即使所请求的程序具有自从启动以来从未运行过,因为可能只需要加载一页内存来满足程序的运行时要求 .

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

相关问题