可以使用哪些技术来加快c++编译时间?

这个问题出现在一些关于Stack Overflow问题c++编程风格的评论中,我很有兴趣听听有什么想法。

我看到过一个相关的问题,为什么c++编译要花这么长时间?,但这并没有提供很多解决方案。


当前回答

有一本书是关于这个主题的,书名是《大规模c++软件设计》(由John Lakos撰写)。

这本书的年代早于模板,所以在书的内容中加上“使用模板也会使编译器变慢”。

其他回答

语言技巧

痘痘成语

看看这里和这里的impl习惯用法,也称为不透明指针或句柄类。它不仅加快了编译速度,还在与非抛出交换函数结合使用时提高了异常安全性。impl习惯用法让您减少了头文件之间的依赖关系,并减少了需要重新编译的数量。

提出声明

尽可能使用前向声明。如果编译器只需要知道SomeIdentifier是一个结构体或指针或其他什么,就不要包含整个定义,这会迫使编译器做更多的工作。这可能会产生级联效应,使这种方式比他们需要的更慢。

I/O流以降低构建速度而闻名。如果你需要它们在头文件中,尝试#包含<iosfwd>而不是<iostream>和#只在实现文件中包含<iostream>头。<iosfwd>报头只保存前向声明。不幸的是,其他标准标头没有各自的声明标头。

在函数签名中首选引用传递而不是值传递。这将消除在头文件中#include各自的类型定义的需要,您只需要向前声明类型。当然,更喜欢const引用而不是非const引用,以避免模糊的错误,但这是另一个问题。

守卫条件

使用保护条件可以防止头文件在一个翻译单元中被多次包含。

#pragma once
#ifndef filename_h
#define filename_h

// Header declarations / definitions

#endif

通过同时使用pragma和ifndef,你可以获得普通宏解决方案的可移植性,以及一些编译器在使用pragma once指令时可以实现的编译速度优化。

减少相互依存

一般来说,你的代码设计越模块化,依赖性越小,你需要重新编译的次数就越少。您还可以减少编译器同时在任何单个块上所做的工作量,因为它需要跟踪的工作量更少了。

编译器选项

预编译头文件

它们用于为多个翻译单元编译包含标题的公共部分。编译器编译它一次,并保存它的内部状态。然后可以快速加载该状态,以便在编译具有相同头集的另一个文件时抢占先机。

注意,在预编译的头文件中只包含很少更改的内容,否则您可能会更频繁地进行完全重建。这是一个存放STL头文件和其他库包含文件的好地方。

Ccache是另一个利用缓存技术来加快速度的实用程序。

使用并行性

许多编译器/ ide支持使用多个内核/ cpu同时进行编译。在GNU Make(通常与GCC一起使用)中,使用-j [N]选项。在Visual Studio中,在首选项下有一个选项,允许它并行构建多个项目。您还可以使用/MP选项进行文件级并行,而不仅仅是项目级并行。

其他并行工具:

Incredibuild 建立统一 distcc

使用较低的优化级别

编译器尝试优化的次数越多,它的工作就越困难。

共享库

将修改频率较低的代码移到库中可以减少编译时间。通过使用共享库(。或者。dll),你也可以减少链接时间。

换一台更快的电脑

更多的RAM,更快的硬盘驱动器(包括ssd),更多的cpu /内核都会使编译速度有所不同。

我有个关于使用内存驱动器的主意。事实证明,对于我的项目来说,这并没有太大的区别。但它们仍然很小。试一试!我很想知道这有多大帮助。

有一种技术在过去对我来说非常有效:不要独立编译多个c++源文件,而是生成一个包含所有其他文件的c++文件,就像这样:

// myproject_all.cpp
// Automatically generated file - don't edit this by hand!
#include "main.cpp"
#include "mainwindow.cpp"
#include "filterdialog.cpp"
#include "database.cpp"

当然,这意味着您必须重新编译所有包含的源代码,以防任何源代码发生更改,因此依赖关系树变得更糟。但是,将多个源文件编译为一个翻译单元更快(至少在我使用MSVC和GCC的实验中),并且生成更小的二进制文件。我还怀疑编译器被赋予了更大的优化潜力(因为它可以一次看到更多代码)。

这种技术在各种情况下都会失效;例如,如果两个或多个源文件声明了同名的全局函数,编译器就会退出。我在其他答案中找不到这个技巧,这就是为什么我在这里提到它。

不管怎样,KDE项目从1999年起就使用了这种完全相同的技术来构建优化的二进制文件(可能是为了发布)。转换到构建配置脚本的过程叫做——enable-final。出于对考古的兴趣,我翻出了宣布这个功能的帖子:http://lists.kde.org/?l=kde-devel&m=92722836009368&w=2

首先,我们必须了解c++与其他语言的不同之处。

有人说c++有太多的特性。但是,有些语言有更多的特性,它们远没有那么慢。

有人说文件的大小很重要。不,源代码行与编译时间无关。

等等,这怎么可能呢?代码行数越多,编译时间就越长,这是怎么回事?

诀窍在于很多代码行隐藏在预处理器指令中。是的。仅仅一个#include就会破坏模块的编译性能。

你看,c++没有模块系统。所有*.cpp文件都是从头编译的。因此,拥有1000 *.cpp文件意味着编译项目1000次。你还有更多?太糟糕了。

这就是为什么c++开发人员不愿意将类拆分为多个文件的原因。所有这些头文件的维护都很乏味。

那么,除了使用预编译的头文件、将所有cpp文件合并为一个文件并保持头文件的数量最小化之外,我们还能做什么呢?

c++ 20给我们带来了模块的初步支持!最终,您将能够忘记#include以及头文件带来的糟糕编译性能。碰过一个文件?只重新编译该文件!需要编译一个新的签出?以秒为单位编译,而不是以分钟和小时为单位。

c++社区应该尽快迁移到c++ 20。c++编译器开发人员应该更加关注这一点,c++开发人员应该开始测试各种编译器的初步支持,并使用那些支持模块的编译器。这是c++历史上最重要的时刻!

如果您有一个多核处理器,Visual Studio(2005及以后版本)和GCC都支持多处理器编译。当然,如果你有硬件,这是可以实现的。