包含库的运行时性能成本是多少?

What is the runtime performance cost of including a library?

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

#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++? 问题,因为这里包含一个 单个 文件。我在这里问,将单个文件包含在源代码中是否与 copy-pasting 实际使用的代码片段有任何不同。我稍微调整了标题以反映这种差异。

这在很大程度上取决于所讨论的库。

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

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

就最终程序而言,没有性能差异。 linker 只会 link 实际用于您的程序的函数。库中未使用的函数将不会得到 linked。

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

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

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

链接器 (ld) 只会 assemble 代码引用的库中的代码,所以如果你有两个函数 ab一个库,但只有对 a 的引用,那么函数 b 可能根本不在最终代码中。

头文件(包括),如果它们仅包含声明,并且如果声明不会导致对库的引用,那么您应该看不出仅输入所需部分之间的任何区别(根据您的示例) 并包括整个头文件。

从历史上看,链接器 ld 会通过文件提取代码,因此只要每个函数 ab 在创建库时位于不同的文件中,就会有完全没有影响。

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

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

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

    例如

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

结论: 值得注意的是,在问什么会使我的程序性能更高的问题之前,您可能应该问问自己您的程序中什么性能更高 案例。您的程序是否打算占用您的大部分 CPU 时间,可能会选择静态 linked 库。如果您的程序只是 运行 偶尔,可能会选择动态 linked 库来减少磁盘使用。同样值得注意的是,使用基于 header 的库只会给你 非常 边缘(如果有的话)静态 linked 二进制文件的性能增益,并且会大大增加你的编译时间。