多源文件可执行比单源文件可执行慢

Posted

技术标签:

【中文标题】多源文件可执行比单源文件可执行慢【英文标题】:Multiple source file executable slower than single source file executable 【发布时间】:2012-03-16 18:52:54 【问题描述】:

我有一个包含所有类定义和函数的源文件。

为了更好地组织,我将类声明 (.h) 和实现 (.cpp) 移动到单独的文件中。

但是当我编译它们时,它导致的可执行文件比我从单一源可执行文件获得的要慢。对于相同的输入,它慢了大约 20-30 秒。我尽力更改任何代码。

为什么会这样?我怎样才能让它再次变得更快?

更新:单源可执行文件在 40 秒内完成,而多源可执行文件需要 60 秒。我指的是运行时而不是编译。

【问题讨论】:

20-30 秒总共多长时间? 另外,您是在命令行上链接/编译,还是使用 VS 之类的 IDE?显然,将源文件链接在一起比将所有内容都放在一个文件中要花费更长的时间,但是对于大型应用程序以外的所有应用程序来说,20-30 秒的差异都太长了。如果我不得不猜测,而这纯粹是猜测,我会说你必须将源代码分散到数千个文件中才能获得这种构建时间。 您确定您没有使用“调试”标志进行编译吗? 20 秒很多 @prelic:他说“(...)它导致的可执行文件比那个(...)慢”。他的意思是 20 秒的运行时间,而不是编译时间。 可执行文件的大小是否具有可比性?无论源代码是如何组织的,编译器都应该产生类似的机器指令。我们可能需要查看一些代码才能了解您遇到问题的原因。由于我们在这里讨论的是运行性能,我猜问题隐藏在代码中的某个地方,而不是您构建它的方式。 【参考方案1】:

我认为,当编译为单个文件时,您的程序运行得更快,因为在这种情况下,编译器有更多信息,需要优化代码。例如,它可以自动内联一些函数,这在单独编译的情况下是不可能的。

为了让它再次变得更快,您可以尝试使用以下选项启用链接时优化器(或整个程序优化器):-flto


如果-flto 选项不可用(并且它仅从 gcc 4.6 开始可用)或者如果您出于某种原因不想使用它,您至少有 2 个选项:

    如果你只拆分你的项目for better organization,你可以创建一个单一的源文件(如all.cxx)和#include所有源文件(所有其他*.cxx文件)到这个文件。然后您只需要构建这个all.cxx,所有编译器优化都可以再次使用。或者,如果您也拆分它以使编译增量,您可以准备 2 个构建选项:增量构建和统一构建。第一个构建所有单独的源,第二个 - 仅all.cxx。查看有关此here 的更多信息。 您可以找到在拆分项目后会降低性能的函数,并将它们移动到使用它们的编译单元或头文件。为此,请从分析开始(请参阅“What can I use to profile C++ code in Linux?”)。进一步调查程序中显着影响程序性能的部分;这里有 2 个选项:或者再次使用分析器来比较增量构建和统一构建的结果(但是这次你需要一个采样分析器,比如 oprofile,而一个检测分析器,比如 gprof,很可能对于这个任务来说太重了);或应用“实验”策略,如gbulmer 所述。

【讨论】:

也这么想。可能是内联。 @questions,是的,gcc 可以进行整个程序优化并打开它,使用-flto 选项。 gcc 和 g++ 只是同一个编译器的 2 个不同名称,分别用于 c 或 c++。 @EvgenyKluev- 我们服务器上的编译器是 4.4.3 版,似乎没有这个选项。还有其他选择吗? @questions,我添加了 2 个其他选项。 如果您碰巧在 XCode 中编写代码,您可以设置“链接时间优化:单体”。这让我恢复了速度:-)【参考方案2】:

这可能与link time optimization 有关。当你的所有代码都在一个源文件中时,编译器对你的代码做什么有更多的了解,因此它可以执行更多的优化。一种这样的优化是内联:编译器只能内联一个函数,如果它在编译时知道它的实现!

这些优化也可以在链接时(而不是编译时)通过将 -flto 标志传递给 gcc 来完成,用于编译和链接阶段(参见 here)。

【讨论】:

您是说-flto 吗?这适用于 g++ 吗? 是的,错字,抱歉。 -flto 也可以与 g++ 一起使用——见鬼,根据手册页,它甚至可以在混合语言时使用。【参考方案3】:

这是一种恢复更快运行时的较慢方法,但如果您想更好地了解导致大变化的原因,您可以做一些“实验”

一个实验是找出哪个函数可能导致大的变化。 为此,您可以“分析”每个函数的运行时间。

例如,使用 GNU gprof,它是 GNU binutils 的一部分: http://www.gnu.org/software/binutils/ 文档地址:http://sourceware.org/binutils/docs-2.22/gprof/index.html

这将测量程序中每个函数所消耗的时间,以及从哪里调用它。进行这些测量可能会产生“海森堡效应”;进行测量将影响程序的性能。因此,您可能想尝试一个实验来找出哪个课程产生的影响最大。

尝试了解运行时在主源代码中包含类源代码和同一程序但单独编译和链接类之间的差异。

要将类实现放入最终程序中,您可以编译并链接它,或者直接将其#include 到“主”程序中,然后编译主程序。

为了更容易尝试排列,您可以使用 #if: 打开或关闭 #include:

#if defined(CLASSA)  // same as #ifdef CLASSA
#include "classa.cpp"
#endif
#if defined(CLASSB)
#include "classb.cpp"
#endif

然后您可以使用编译器的命令行标志来控制哪些文件被#included,例如

g++ -DCLASSA -DCLASSB ... main.c classc.cpp classd.cpp classf.cpp  

生成 -Dflags 和链接命令的排列可能只需要几分钟。然后,您将有一种方法可以生成“在一个单元中”编译与单独链接的所有排列,然后运行(和时间)每个排列。

我假设你的头文件被包装在通常的

#ifndef _CLASSA_H_
#define _CLASSA_H_
//...
#endif

然后,您将获得有关该课程的一些重要信息。

这些类型的实验可能会对程序和编译器的行为产生一些见解,这可能会激发一些其他改进的想法。

【讨论】:

以上是关于多源文件可执行比单源文件可执行慢的主要内容,如果未能解决你的问题,请参考以下文章

理解Unix可执行文件

Mac 命令行中添加命令直接调用可执行文件

linux当前目录下的可执行文件不能直接执行的原因

UBUNTU 8.04的可执行文件后缀是啥??

可执行文件 = 可执行文件 python 错误(尝试使用 cx_freeze 将 pygame 转换为可执行文件)

java的可执行文件jar用java代码执行方法。