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

时间:2012-03-16 18:52:55

标签: c++ optimization gcc

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

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

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

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

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

3 个答案:

答案 0 :(得分:10)

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

要再次使其更快,您可以尝试使用此选项启用链接时优化程序(或整个程序优化程序):-flto


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

  1. 如果仅拆分项目for better organization,则可以创建单个源文件(如all.cxx)和#include所有源文件(所有其他*.cxx文件)这个文件。然后,您只需要构建此all.cxx,并且所有编译器优化都可以再次使用。或者,如果您将其拆分以进行编译增量,则可以准备2个构建选项:增量构建和统一构建。第一个构建所有单独的源,第二个构建 - 仅all.cxx。请参阅有关此here
  2. 的更多信息
  3. 您可以找到在拆分项目后牺牲性能的功能,并将它们移动到编译单元(使用它们)或头文件。为此,请从分析开始(请参阅“What can I use to profile C++ code in Linux?”)。进一步调查该计划的部分内容,这对计划的绩效产生重大影响;这里有两个选项:再次使用分析器来比较增量和统一构建的结果(但这次你需要一个采样分析器,比如oprofile,而像仪器分析器,像gprof,很可能,对于这个任务太重了);或者应用'{3}}所描述的'实验'策略。

答案 1 :(得分:5)

这可能与link time optimization有关。当您的所有代码都在单个源文件中时,编译器会更多地了解您的代码所执行的操作,以便执行更多优化。其中一个优化是内联:如果编译器在编译时知道它的实现,编译器只能内联函数!

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

答案 2 :(得分:1)

回到更快的运行时间这是一种较慢的方法,但是如果你想更好地理解引起大变化的原因,你可以做一些'实验'

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

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

这将测量程序中每个函数所消耗的时间以及调用它的位置。进行这些测量可能会产生“海森堡效应”;进行测量会影响程序的性能。所以你可能想尝试一个实验来找出哪个类产生了最大的差异。

尝试了解主源中的类源代码与同一个程序之间的运行时间如何变化,以及单独编译和链接的类。

要将类实现放入最终程序,您可以编译并链接它,或者只需将它包含到'main'程序中,然后编译main。

为了更容易尝试排列,您可以使用#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

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

这些类型的实验可能会对程序和编译器的行为有所了解,这可能会刺激其他一些改进的想法。