c++ - g++ 在多个文件上与使用 Google 模拟的单一文件相比要慢得多
问题描述
我遇到了一个似乎与 g++ 相关的问题。基本上,当一个程序被拆分成多个文件时,g++ 编译一个程序要比一个单一的单一文件花费更多的时间。事实上,如果将单个文件组合在一起并编译,它的运行速度要比在 g++ 命令行中列出单个文件快得多。比如9个文件,编译需要1分39秒;当我将它们组合在一起时,编译只需 13 秒。我试过使用strace
,但它只是卡住了cc1plus
;当我使用该-f
选项时,我仍然无法找出导致问题的原因。
我已经隔离了问题。以下是如何重现它。我写了一个非常简单的程序,如下所示:
void func_01(int i)
{
int j;
volatile int *jp;
jp = &j;
for (; i; i--) ++*jp;
}
void call_01(void)
{
func_01(10000);
}
int main(int argc, char *argv[])
{
call_01();
}
然后我复制了它,删除了 main 并替换了越来越多的数字,999 次。然后我建立了:
% time g++ -c test*.cpp
real 0m18.919s
user 0m10.208s
sys 0m5.595s
% cat test*.cpp > mon.cpp
% time g++ -c mon.cpp
real 0m0.824s
user 0m0.776s
sys 0m0.040s
因为我打算扩展到数百个比这复杂得多的文件,所以缩短构建时间很重要。任何人都可以帮助解释为什么会发生这种情况,或者提供一个不太严重的解决方法吗?我认为这部分与预处理器和包含保护引起的节省有关,因为即使我包含一个文件,时间差也会显着增加(在一种情况下为五倍)但它仍然存在,没有包含,使用整体文件要快 20 倍。
g++ 的版本是 4.4.2,但我检查了最新版本,8.2.0,它也存在。
解决方案
有两种不同的效果:
编译器调用开销:编译器是复杂的可执行文件,有时它们甚至被拆分为前端和后端可执行文件,并且前端为每个单独的源文件生成后端,即使所有源文件都传递给前端的同一个编译器调用。例如,Gcc 和 llvm 就是这样做的。
- 指定
g++ -v
以查看这些冗余编译器调用。这回答了您的主要问题,我想为什么即使没有头文件也会发生这种情况。
- 指定
由于从头开始一遍又一遍地解析和编译相同的头文件而产生的开销。在实际示例中,此标头开销将比编译器调用本身重要得多。
因为如果我什至包含一个文件,时间差就会显着增加(在一种情况下是五倍)
是的!这也可能慢 1000 倍而不是 5 倍。对于模板密集型代码,编译器需要在编译时做很多事情。
跨多个源文件拆分时的减速会打击您,尤其是对于 C++ 代码,因为 C++ 是标头密集型的。您的所有源代码*.cpp
都是单独编译的,并且它们包含的所有头文件都包含在每个单独的源文件中。
现在,如果您将所有源文件集中在一起,那么正如您所说,由于包含保护,所有标头都只解析一次。由于编译器花费了大部分时间来解析和编译头文件,这非常重要,尤其是对于模板繁重的代码(例如,使用 STL 就足够了)。
手写 C++ 源代码和生成的 C++ 源代码的源文件数量是以下之间的权衡:
我的完全重建时间很快,但我的增量构建时间很慢。
- 当您只有一个源文件(即 *.cpp 文件)或非常少的源文件时,就会出现这种情况。
我的完整构建时间很慢,但我的增量构建时间很快。
- 当您有很多小源文件(即 *.cpp 文件)时,就会出现这种情况。
(无论如何,头文件的数量并不重要(除非你总是引入太多冗余的东西。这是关于编译器调用的数量,即 *.cpp 或 *.o 文件的数量。)
1. 从头开始的完整编译时间很短,因为编译器只看到所有头文件一次,这在 C++ 中很重要,尤其是基于模板的仅头文件(或头文件密集型)库,如 STL 或 boost。
2. 单独的编译时间很快,因为当仅更改数百个文件中的一个时,只需编译 *.cpp 文件中的极少数代码。
这在很大程度上取决于您的用例。
如果您生成 C++ 代码,您应该在生成器中添加一个选项,以允许用户选择哪种方式进行此权衡。
推荐阅读
- android - 如何在改造中将此 json 解析为 gson
- python - 尝试和除了返回值 Python
- java - 捕获oracle数据库插入的数据到spring boot应用
- thunderbird - Thunderbird78+:如何检查消息创建、回复和转发
- sockets - 部署后连接失败 simple-peer
- python - LinkedIn 公司搜索 API
- python - EmailMultiAlternatives 不发送密件抄送电子邮件
- c - (C) 我插入的文本与我插入的文本读法不同
- javascript - Rails 5/6 和 Webpacker 的大型(2M~)包的最佳实践?
- php - 使用 php 框架之一运行 wordpress