【问题标题】:Does source code amalgamation really increase the performances of a C or C++ program? [closed]源代码合并真的可以提高 C 或 C++ 程序的性能吗? [关闭]
【发布时间】:2016-08-11 14:20:45
【问题描述】:

代码合并是将整个源代码复制到一个文件中。

例如,SQLite 可以减少编译时间并提高生成的可执行文件的性能。在这里,它产生了一个包含 184K 行代码的文件。

我的问题不是关于编译时间(已经在this question 中回答),而是关于可执行文件的效率。

SQLite 开发者说:

除了让 SQLite 更容易整合到其他项目中之外,合并还使它运行得更快。当代码包含在单个翻译单元中(例如合并中)时,许多编译器能够对代码进行额外的优化。当我们使用合并来编译 SQLite 而不是单独的源文件时,我们测量到了 5% 到 10% 的性能改进。这样做的缺点是额外的优化通常采用函数内联的形式,这往往会使生成的二进制图像的大小更大。

据我了解,这是由于interprocedural optimization (IPO),编译器进行的优化。

GCC 开发者也这么说(感谢@nwp 提供链接):

编译器根据它对程序的了解执行优化。一次将多个文件编译为单个输出文件模式允许编译器在编译每个文件时使用从所有文件中获取的信息。

但他们没有谈论最终的收获。

除了 SQLite 的那些测量之外,是否有任何测量证实或反驳 IPO with amalgamation 在使用 gcc 编译时比 IPO without amalgamation 产生更快的可执行文件的说法?

作为一个附带问题,关于此优化,进行代码合并或将所有 .cpp(或 .c)文件#include 到一个文件中是否相同?

【问题讨论】:

  • 哦,这是一个讨论式的问题。它不适合这个网站。
  • 编译速度优势值得怀疑,因为您不能进行增量构建。如果工具链允许良好的链接时优化,编译的结果也不一定要好得多。
  • @Olaf 这里“C/C++”的意思是“C 或C++ 语言”,而不是“C/C++ 语言”。根据您的说法,哪个网站更适合此类问题?由于已经有相关问题,我认为这是一个不错的网站。
  • 您提到的缺点也适用于多文件源分发(A 必须重新编译 + 发布以合并 B 的新版本)。作为“A”应用程序创建者,您通常对部署环境几乎没有控制权,因此您要么制作一个非常聪明的安装程序来处理依赖项并根据需要安装额外的软件(Linux 软件包管理器,如 Debian 上的 dpkg),要么您必须将所有内容与“A”一起分发。编译时依赖和捆绑依赖之间的区别在于,通过安装第一个依赖,您可以减少对主机的污染并减少冲突的风险。
  • 在寻找一种工具来为库创建头文件的合并时看到了这一点。我对性能优势不感兴趣;只是包含文件的可移植性。 100% 正确,至少在较旧的 C++ 编译器中,如果您创建了一个合并,您可以获得更优化的代码。为什么必须在使用函数之前声明它们?较新的编译器/生成器,不需要您声明函数,如 C#,可能会获得较少的好处。但是,他们在构建时对您的源进行了两次传递。今天,这个问题可能比实际更学术。

标签: c++ c gcc compiler-optimization


【解决方案1】:

源代码文件的组织不会“产生更高效的二进制文件”,从多个源文件中检索的速度可以忽略不计。

版本控制系统将获取任何文件的deltas,无论文件大小。

通常,像这些单独的组件被单独编译以生成包含相关目标代码的二进制:源代码不会每次都重新编译。当“应用程序 A”使用已更改的“库 B”时,必须重新链接“应用程序 A”,但如果库的 API 未更改,则不必重新编译。

而且,就库本身而言,如果它由(数百个)单独的源文件组成,则在重新链接库之前必须重新编译已更改的文件。 (任何Makefile 都会这样做。)如果源代码是“一件大事”,那么您每次都必须重新编译所有代码,这可能需要很长 时间.. . 基本上,浪费时间。

有两种方法可以将库中的目标代码(一旦构建...)合并到可执行文件中:静态链接和动态。 如果使用静态链接,库的必要部分将被复制到可执行文件中......但不是全部。运行可执行文件时,库文件不必存在。

如果使用动态链接,则整个库存在于一个单独的文件中(例如.DLL.so确实必须在运行时存在,但将由同时使用它的每个应用程序共享。

我建议您主要将此视为源代码管理问题,而不是会带来任何技术或运行时优势的问题。 (它不会。)我发现很难找到一个令人信服的理由这样做。

【讨论】:

  • 我不同意微不足道的运行时优势。有很多 C 编译器不支持 LTO。通过将所有代码编译到一个文件中,这些编译器可以实现未使用的函数、内联函数和其他优化。
  • 还请记住,您会面临与他人不同步的源文件(大小)的严重风险。哎呀,源代码更改并没有应用于那个巨大源文件的每个副本。你明白了......就像我说的,“源代码管理问题。”
  • 静态库确实非常好,包括“从安全的角度来看”,因为它的必要部分由链接器找到并组合在一起,并合并直接进入可执行文件。这不是全有或全无:链接器足够聪明,可以选择它需要的东西。这一切都进入了当时的可执行文件。 “现在,对那只小狗进行代码签名,你就有了无法篡改的东西。它是独立的。”
  • @TomCornebize:那它不共享,不是吗? “共享”意味着一个库在不同的程序之间共享。为一个应用程序生成一个“共享库”几乎没有意义。
  • @Olaf,当 static 库更新时,所有引用它的应用程序都必须重新链接。完全避免了“(重新)编译源代码”的开销,但重新链接受影响的可执行文件的义务却没有。这从根本上改变了可执行文件并替换了其中包含的一些目标代码。 相反, 当一个 动态 库被更新时,包含它的单个文件只是被替换。使用该库的可执行文件不需要重新编译(除非 API 已更改),因为它们包含它的任何目标代码。
猜你喜欢
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 2021-03-27
  • 2021-01-04
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
相关资源
最近更新 更多