代码之家  ›  专栏  ›  技术社区  ›  Eloff

静态链接与动态链接

  •  367
  • Eloff  · 技术社区  · 16 年前

    在某些情况下,选择静态链接而不是动态链接是否有令人信服的性能原因?我听过或读过下面的文章,但我对这个问题的了解不够,不能保证它的真实性。

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

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

    15 回复  |  直到 7 年前
        1
  •  323
  •   MasterMastic    11 年前
    • 动态的 连接罐 降低总资源消耗 (如果多个进程共享同一个库(当然包括“相同”中的版本))。我相信这正是促使它在大多数环境中存在的原因。这里的“资源”包括磁盘空间、RAM和缓存空间。当然,如果动态链接器不够灵活,则存在 DLL hell .
    • 动态的 链接意味着缺陷修复和库升级 传播 改进 你的 产品无需发货。
    • 插件 总是呼吁 动态 链接。
    • 静态的 链接,意味着您可以知道代码将在 有限的环境 (在启动过程的早期,或在救援模式中)。
    • 静态的 链接可以生成二进制文件 更容易分发 到不同的用户环境(以发送一个更大、更需要资源的程序为代价)。
    • 静态的 链接可能会稍微允许 快速启动 但这在某种程度上取决于程序的大小和复杂性 关于OSS加载策略的详细信息。

    一些编辑将非常相关的建议包括在评论和其他答案中。我想指出的是,你在这方面的突破很大程度上取决于你计划在什么环境中运行。最小的嵌入式系统可能没有足够的资源来支持动态链接。稍大一点的小型系统可能很好地支持链接,因为它们的内存足够小,使得动态链接节省的RAM非常有吸引力。正如马克所指出的,全套消费型PC拥有巨大的资源,你可能会让便利性问题驱使你思考这个问题。


    要解决性能和效率问题: 它取决于 .

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

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

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

    注意,静态与动态链接是传统的 一个优化问题,因为它们都涉及到对象文件的单独编译。但是,这不是必需的:原则上,编译器可以先将“静态库”编译为消化的ast形式,然后通过将这些ast添加到为主代码生成的ast中来“链接”它们,从而实现全局优化。我使用的所有系统都没有这样做,所以我无法评论它的工作情况。

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

        2
  •  63
  •   Mark Ransom    16 年前

    动态链接是满足某些许可证要求(如 LGPL .

        3
  •  63
  •   Lothar    8 年前

    1)基于调用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一直是一个问题,需要嵌入蹦床。

    但在流行的x86操作系统(如Linux)上,您不需要关心是否使用 gcc 转换 -fpic (强制使用间接跳转表)。因为如果不这样做,代码就会像普通的链接器一样被修复。但是这样做会使代码段不可共享,并且需要将代码从磁盘映射到内存中,并在使用之前触摸所有代码(清空大部分缓存,点击TLB)等。有一段时间,这被认为是缓慢的……太慢了。

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

    我不记得操作系统(solaris或freebsd)给我的Unix构建系统带来了什么问题,因为我没有这样做,我想知道为什么它会崩溃,直到我应用 -fPIC 海湾合作委员会 .

        4
  •  42
  •   stakx - no longer contributing Saravana Kumar    16 年前

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

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

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

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

    这种情况通常发生在那些深埋在共享libs中的符号有一个拼写错误的名称,因此不会静态链接的时候。动态链接器不会完全解析所有符号,不管使用深度优先还是宽度优先评估,因此您可以使用没有完全关闭的动态链接可执行文件来完成。

        6
  •  21
  •   AProgrammer    16 年前

    1/我曾参与过一些项目,其中动态链接与静态链接是基准,但差异还没有确定到足以转换为动态链接的程度(我不是测试的一部分,我只是知道结论)。

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

    3/我还参与过其他项目,这些项目使用了“轻松补丁”功能的动态链接。但是这个“简单补丁”使得小补丁的分发变得更加简单,而复杂补丁的分发则是版本控制的噩梦。我们经常不得不推送所有东西,还要跟踪客户站点的问题,因为错误的版本是令牌。

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

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

    • 当共享是重要的(大型图书馆同时使用多个进程,如C/C++运行时,GUI库,…它们通常是独立管理的,ABI是严格定义的)

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

        7
  •  20
  •   nos    16 年前

    This 详细讨论Linux上的共享库和性能实现。

        8
  •  10
  •   Jonathan Leffler    16 年前

    在类Unix的系统上,动态链接可能会使“根”使用安装在偏远位置的共享库的应用程序变得困难。这是因为对于具有根权限的进程,动态链接器通常不会注意ld_library_path或其等效项。有时候,静态链接可以节省一天的时间。

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

        9
  •  10
  •   Hans Passant    7 年前

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

        10
  •  7
  •   Thomas Matthews    16 年前

    动态链接需要额外的时间让操作系统找到动态库并加载它。通过静态链接,所有东西都在一起,这是对内存的一次性加载。

    此外,参见 DLL Hell . 在这种情况下,OS加载的dll不是应用程序附带的dll,也不是应用程序期望的版本。

        11
  •  7
  •   Arne    10 年前

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

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

        12
  •  5
  •   R Samuel Klatchko    16 年前

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

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

    通过动态链接,只需重新构建和重新分配动态库,即可完成。

        13
  •  3
  •   Govardhan Murali    9 年前

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

        14
  •  2
  •   Greg A. Woods    8 年前

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

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

    一个非常常见的例子是使用GNU/Linux系统的设备 Busybox .我把这个带到了极端 NetBSD 通过构建一个可引导的i386(32位)系统映像,其中包含内核及其根文件系统,后者包含一个静态链接 crunchgen )带有硬链接的二进制文件,指向其自身包含的所有程序 全部的 (最后统计274个)标准全功能系统程序(除了工具链以外的大多数程序),它小于20个 兆欧 字节大小(在只有64MB内存的系统中可能运行得非常舒适(即使根文件系统是未压缩的,并且完全在RAM中),尽管我找不到这么小的字节来测试它。

    在前面的文章中已经提到,静态链接二进制文件的启动时间更快(它可以是 许多 更快),但这只是图片的一部分,尤其是当所有对象代码都链接到同一个文件时,尤其是当操作系统支持直接从可执行文件请求代码分页时。在这个理想的场景中,程序的启动时间是 字面 可以忽略不计,因为几乎所有的代码页都已经在内存中,并且被shell使用(和 init 任何其他可能正在运行的后台进程),即使自启动后请求的程序从未运行过,因为可能只需要加载一页内存即可满足程序的运行时要求。

    然而,这还不是全部。我还通常通过静态链接所有二进制文件,为我的完整开发系统构建和使用netbsd操作系统安装。尽管这会占用大量的磁盘空间(对于x86_64,包括toolchain和x11静态链接在内的所有程序,总共约6.6GB)(尤其是如果一个程序保持完整的调试符号表可供所有程序使用,另一个约2.5GB),但总体而言,结果仍然运行得更快,对于某些任务,甚至比典型的动态链接系统占用更少的内存。声称共享库代码页的STEM。磁盘是便宜的(甚至是快磁盘),内存缓存频繁使用的磁盘文件也是相对便宜的,但CPU的周期确实不是,而且支付 ld.so 启动的每个过程的启动成本 每一个 它的启动时间需要几个小时和几个小时的CPU周期,而这些任务需要启动许多进程,特别是当相同的程序反复使用时,例如开发系统上的编译器。静态链接的工具链程序可以通过以下方式减少整个操作系统的多体系结构构建时间: 小时 . 我还没有在我的单曲中构建工具链 克朗根 'ed binary,但是我怀疑当我这样做的时候,会有更多的构建时间节省,因为CPU缓存的胜利。

        15
  •  1
  •   Nykal    8 年前

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

    动态链接是您通常认为的,它使可执行文件仍然需要dlls,并且这样的文件在同一目录中(或者dlls可能在系统文件夹中)。

    (DLL= 动态链接 图书馆)

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

    推荐文章