【问题标题】:What is the runtime performance cost of including a library? [duplicate]包含库的运行时性能成本是多少? [复制]
【发布时间】:2016-08-27 07:18:43
【问题描述】:

在包含整个库(可能包含数百个函数)和仅使用单个函数之间是否存在任何运行时性能差异

#include<foo>

int main(int argc, char *argv[]) {
    bar();//from library foo
    return 0;
}

以及将库中的相关代码片段直接粘贴到代码中,例如:

void bar() {
...
}

int main(int argc, char *argv[]) {
    bar();//defined just above
    return 0;
}

什么会阻止我在 C 文件的开头盲目地包含所有我最喜欢(和最常用)的库?这个流行的线程C/C++: Detecting superfluous #includes? 表明编译时间会增加。但是编译后的二进制文件会有什么不同吗?第二个程序实际上会胜过第一个程序吗?


相关:what does #include <stdio.h> really do in a c program

编辑:这里的问题与相关的Will there be a performance hit on including unused header files in C/C++? 问题不同,因为这里包含一个 single 文件。我在这里询问是否包含单个文件与将实际使用的代码片段复制粘贴到源代码中是否有任何不同。我稍微调整了标题以反映这种差异。

【问题讨论】:

  • 我的经验法则是只包含您需要的内容。如果您包含您不需要的内容,您可能会使人们感到困惑,因为他们可能认为您包含它是有原因的,而您实际上并没有。
  • 您所展示的 - 包括标题。标头主要由 声明 组成,它们根本不会影响性能。在这里你最多可以破坏的是编译时间。但是说到库 - 一个像样的链接器无论如何都会消除所有未使用的东西。
  • @Eugene 你应该把这个作为答案! ;)
  • 1.正如其他人所提到的,您包含的头文件仅与链接器最终可能加载的库部分相关。 2. 如果您使用静态链接,链接器将仅链接您实际使用/需要的模块。 (是否有任何额外的内容取决于库实现者是否为每个模块构建了一个或多个函数。) 3. 如果您使用动态链接(现在更常见),您“获得”整个库,但是它通常 (a) 按需分页和 (b) 与其他进程共享,因此您无需承担全部费用。

标签: c++ c performance include


【解决方案1】:

就最终程序而言,没有性能差异。链接器只会链接实际用于您的程序的函数。库中未使用的函数将不会被链接。

如果你包含很多库,编译程序可能需要更长的时间。

不应包含所有“最喜欢的库”的主要原因是程序设计。您的文件不应包含除了它正在使用的资源之外的任何内容,以减少文件之间的依赖关系。您的文件对程序其余部分的了解越少越好。它应该尽可能自主。

【讨论】:

  • 假设你正在编写一个链表 ADT。常识表明,不应该强迫每个使用您喜欢的列表的人包括数学库、SQL 数据库、3D 图形等。在进行程序设计时应用常识会让您走得更远。
  • 如果库接口没有使用上述库中定义的任何类型,没有理由“强制”用户包含它们,对吧?但如果是,并且随附的标头不包括它们,那么从任何意义上讲,它都不是正确编写的标头。
  • @EugeneSh。如果是这样的话,图书馆就会被破坏。
  • "链接器只会链接实际用于您的程序的函数。"这当然取决于链接器,也取决于库的构建方式。如果一个库源只有 1 个包含许多函数的 .c 文件,则使用 1 个函数可能会得到整个包。
【解决方案2】:

这不是一个简单的问题,因此不值得一个简单的答案。在确定哪种性能更高时,您可能需要考虑许多事项。

  1. 您的编译器和链接器:不同的编译器将以不同的方式进行优化。这是很容易被忽视的,并且在进行概括时可能会导致一些问题。在大多数情况下,现代编译器和链接器将优化二进制文件,使其仅包含 100% 执行所需的内容。但是,并非所有编译器都会优化您的二进制文件。
  2. 动态链接:使用其他库时有两种类型的链接。它们的行为方式相似,但根本不同。当您链接到动态库时,该库将与程序保持分离,并且仅在运行时执行。动态库通常被称为共享库,因此应该被视为被多个二进制文件使用。因为这些库通常是共享的,所以链接器不会从库中删除任何功能,因为链接器不知道该系统或操作系统中的所有二进制文件都需要该库的哪些部分。因此,与动态库链接的二进制文件对性能的影响很小,尤其是在启动程序之后。这种性能影响将随着动态链接的数量而增加。
  3. 静态链接:当您将二进制文件链接到静态库(使用优化链接器)时,链接器将“知道”您需要从该特定库中获得哪些功能,并将删除不需要的功能在生成的二进制文件中使用。正因为如此,二进制文件将变得更高效,因此性能更高。然而,这确实是有代价的。

    例如

    假设您有一个操作系统,该操作系统在整个系统的大量二进制文件中广泛使用了一个库。如果您将该库构建为共享库,则所有二进制文件都将共享该库,同时可能使用不同的功能。现在假设您将每个二进制文件与库静态链接。您最终会得到二进制功能的大量重复,因为每个二进制文件都会从该库中获得所需功能的副本。

结论:值得注意的是,在问什么会使我的程序性能更高的问题之前,您可能应该问自己在您的情况下什么性能更高。您的程序是否打算占用您的大部分 CPU 时间,可能会使用静态链接库。如果您的程序只是偶尔运行,可能会使用动态链接库来减少磁盘使用量。还值得注意的是,与静态链接的二进制文件相比,使用基于标头的库只会为您提供非常边际(如果有的话)性能增益,并且会大大增加您的编译时间。

【讨论】:

【解决方案3】:

这在很大程度上取决于库和它们的结构,并且可能取决于编译器的实现。

链接器 (ld) 只会从代码引用的库中汇编代码,因此如果您在库中有两个函数 ab,但只有对 a 的引用,那么函数b 可能根本不在最终代码中。

头文件(包括),如果它们只包含声明,并且如果声明不会导致对库的引用,那么您应该不会看到仅输入您需要的部分(根据您的示例)和包括整个头文件。

从历史上看,链接器 ld 会按文件提取代码,因此,当创建库时,每个函数 ab 都位于不同的文件中,根本不会产生任何影响。

但是,如果库没有仔细构建,或者编译器实现确实从库中提取了每一位代码(无论是否需要),那么您可能会对性能产生影响,因为您的代码会更大并且可能更难放入 CPU 缓存中,并且 CPU 执行管道有时不得不等待从主内存而不是从缓存中获取指令。

【讨论】:

    【解决方案4】:

    这在很大程度上取决于相关库。

    他们可能会初始化全局状态,这会减慢程序的启动和/或关闭速度。或者他们可能会启动与您的代码并行执行某些操作的线程。如果您有多个线程,这也可能会影响性能。

    一些库甚至可能修改现有的库函数。可能是为了收集有关内存或线程使用情况的统计信息,或者出于安全审计目的。

    【讨论】:

    • 库可以做任何主程序没有告诉它做的事情吗??
    • @EugeneSh。来自主程序的函数调用可能会导致库中的内部函数调用。如果您的程序没有调用库中的任何函数,则库中的任何代码都不会被链接或执行。
    • @Lundin 当然。但在这种情况下,你不能说它们没有被使用。无论如何,您必须将它们链接起来。
    • C++ 库可以包含具有重要构造函数的全局对象。仅链接库可能会导致此代码运行。这取决于工具链和链接器选项。不过,它通常确实需要代码明确使用库中的至少一个符号。
    猜你喜欢
    • 2011-06-20
    • 2015-04-21
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2018-02-16
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多