What techniques can be used to speed up C++ compilation times?

Viewed 133222

What techniques can be used to speed up C++ compilation times?

This question came up in some comments to Stack Overflow question C++ programming style, and I'm interested to hear what ideas there are.

I've seen a related question, Why does C++ compilation take so long?, but that doesn't provide many solutions.

27 Answers

Language techniques

Pimpl Idiom

Take a look at the Pimpl idiom here, and here, also known as an opaque pointer or handle classes. Not only does it speed up compilation, it also increases exception safety when combined with a non-throwing swap function. The Pimpl idiom lets you reduce the dependencies between headers and reduces the amount of recompilation that needs to be done.

Forward Declarations

Wherever possible, use forward declarations. If the compiler only needs to know that SomeIdentifier is a struct or a pointer or whatever, don't include the entire definition, forcing the compiler to do more work than it needs to. This can have a cascading effect, making this way slower than they need to be.

The I/O streams are particularly known for slowing down builds. If you need them in a header file, try #including <iosfwd> instead of <iostream> and #include the <iostream> header in the implementation file only. The <iosfwd> header holds forward declarations only. Unfortunately the other standard headers don't have a respective declarations header.

Prefer pass-by-reference to pass-by-value in function signatures. This will eliminate the need to #include the respective type definitions in the header file and you will only need to forward-declare the type. Of course, prefer const references to non-const references to avoid obscure bugs, but this is an issue for another question.

Guard Conditions

Use guard conditions to keep header files from being included more than once in a single translation unit.

#pragma once
#ifndef filename_h
#define filename_h

// Header declarations / definitions

#endif

By using both the pragma and the ifndef, you get the portability of the plain macro solution, as well as the compilation speed optimization that some compilers can do in the presence of the pragma once directive.

Reduce interdependency

The more modular and less interdependent your code design is in general, the less often you will have to recompile everything. You can also end up reducing the amount of work the compiler has to do on any individual block at the same time, by virtue of the fact that it has less to keep track of.

Compiler options

Precompiled Headers

These are used to compile a common section of included headers once for many translation units. The compiler compiles it once, and saves its internal state. That state can then be loaded quickly to get a head start in compiling another file with that same set of headers.

Be careful that you only include rarely changed stuff in the precompiled headers, or you could end up doing full rebuilds more often than necessary. This is a good place for STL headers and other library include files.

ccache is another utility that takes advantage of caching techniques to speed things up.

Use Parallelism

Many compilers / IDEs support using multiple cores/CPUs to do compilation simultaneously. In GNU Make (usually used with GCC), use the -j [N] option. In Visual Studio, there's an option under preferences to allow it to build multiple projects in parallel. You can also use the /MP option for file-level paralellism, instead of just project-level paralellism.

Other parallel utilities:

Use a Lower Optimization Level

The more the compiler tries to optimize, the harder it has to work.

Shared Libraries

Moving your less frequently modified code into libraries can reduce compile time. By using shared libraries (.so or .dll), you can reduce linking time as well.

Get a Faster Computer

More RAM, faster hard drives (including SSDs), and more CPUs/cores will all make a difference in compilation speed.

I'd recommend these articles from "Games from Within, Indie Game Design And Programming":

Granted, they are pretty old - you'll have to re-test everything with the latest versions (or versions available to you), to get realistic results. Either way, it is a good source for ideas.

I will just link to my other answer: How do YOU reduce compile time, and linking time for Visual C++ projects (native C++)?. Another point I want to add, but which causes often problems is to use precompiled headers. But please, only use them for parts which hardly ever change (like GUI toolkit headers). Otherwise, they will cost you more time than they save you in the end.

Another option is, when you work with GNU make, to turn on -j<N> option:

  -j [N], --jobs[=N]          Allow N jobs at once; infinite jobs with no arg.

I usually have it at 3 since I've got a dual core here. It will then run compilers in parallel for different translation units, provided there are no dependencies between them. Linking cannot be done in parallel, since there is only one linker process linking together all object files.

But the linker itself can be threaded, and this is what the GNU gold ELF linker does. It's optimized threaded C++ code which is said to link ELF object files a magnitude faster than the old ld (and was actually included into binutils).

There's an entire book on this topic, which is titled Large-Scale C++ Software Design (written by John Lakos).

The book pre-dates templates, so to the contents of that book add "using templates, too, can make the compiler slower".

Once you have applied all the code tricks above (forward declarations, reducing header inclusion to the minimum in public headers, pushing most details inside the implementation file with Pimpl...) and nothing else can be gained language-wise, consider your build system. If you use Linux, consider using distcc (distributed compiler) and ccache (cache compiler).

The first one, distcc, executes the preprocessor step locally and then sends the output to the first available compiler in the network. It requires the same compiler and library versions in all the configured nodes in the network.

The latter, ccache, is a compiler cache. It again executes the preprocessor and then check with an internal database (held in a local directory) if that preprocessor file has already been compiled with the same compiler parameters. If it does, it just pops up the binary and output from the first run of the compiler.

Both can be used at the same time, so that if ccache does not have a local copy it can send it trough the net to another node with distcc, or else it can just inject the solution without further processing.

Here are some:

  • Use all processor cores by starting a multiple-compile job (make -j2 is a good example).
  • Turn off or lower optimizations (for example, GCC is much faster with -O1 than -O2 or -O3).
  • Use precompiled headers.

When I came out of college, the first real production-worthy C++ code I saw had these arcane #ifndef ... #endif directives in between them where the headers were defined. I asked the guy who was writing the code about these overarching things in a very naive fashion and was introduced to world of large-scale programming.

Coming back to the point, using directives to prevent duplicate header definitions was the first thing I learned when it came to reducing compiling times.

More RAM.

Someone talked about RAM drives in another answer. I did this with a 80286 and Turbo C++ (shows age) and the results were phenomenal. As was the loss of data when the machine crashed.

Use

#pragma once

at the top of header files, so if they're included more than once in a translation unit, the text of the header will only get included and parsed once.

Use forward declarations where you can. If a class declaration only uses a pointer or reference to a type, you can just forward declare it and include the header for the type in the implementation file.

For example:

// T.h
class Class2; // Forward declaration

class T {
public:
    void doSomething(Class2 &c2);
private:
    Class2 *m_Class2Ptr;
};

// T.cpp
#include "Class2.h"
void Class2::doSomething(Class2 &c2) {
    // Whatever you want here
}

Fewer includes means far less work for the preprocessor if you do it enough.

Just for completeness: a build might be slow because the build system is being stupid as well as because the compiler is taking a long time to do its work.

Read Recursive Make Considered Harmful (PDF) for a discussion of this topic in Unix environments.

I had an idea about using a RAM drive. It turned out that for my projects it doesn't make that much of a difference after all. But then they are pretty small still. Try it! I'd be interested in hearing how much it helped.

  • Upgrade your computer

    1. Get a quad core (or a dual-quad system)
    2. Get LOTS of RAM.
    3. Use a RAM drive to drastically reduce file I/O delays. (There are companies that make IDE and SATA RAM drives that act like hard drives).
  • Then you have all your other typical suggestions

    1. Use precompiled headers if available.
    2. Reduce the amount of coupling between parts of your project. Changing one header file usually shouldn't require recompiling your entire project.

Where are you spending your time? Are you CPU bound? Memory bound? Disk bound? Can you use more cores? More RAM? Do you need RAID? Do you simply want to improve the efficiency of your current system?

Under gcc/g++, have you looked at ccache? It can be helpful if you are doing make clean; make a lot.

Dynamic linking (.so) can be much much faster than static linking (.a). Especially when you have a slow network drive. This is since you have all of the code in the .a file which needs to be processed and written out. In addition, a much larger executable file needs to be written out to the disk.

Starting with Visual Studio 2017 you have the capability to have some compiler metrics about what takes time.

Add those parameters to C/C++ -> Command line (Additional Options) in the project properties window: /Bt+ /d2cgsummary /d1reportTime

You can have more informations in this post.

If you have a multicore processor, both Visual Studio (2005 and later) as well as GCC support multi-processor compiles. It is something to enable if you have the hardware, for sure.

First of all, we have to understand what so different about C++ that sets it apart from other languages.

Some people say it's that C++ has many too features. But hey, there are languages that have a lot more features and they are nowhere near that slow.

Some people say it's the size of a file that matters. Nope, source lines of code don't correlate with compile times.

But wait, how can it be? More lines of code should mean longer compile times, what's the sorcery?

The trick is that a lot of lines of code is hidden in preprocessor directives. Yes. Just one #include can ruin your module's compilation performance.

You see, C++ doesn't have a module system. All *.cpp files are compiled from scratch. So having 1000 *.cpp files means compiling your project a thousand times. You have more than that? Too bad.

That's why C++ developers hesitate to split classes into multiple files. All those headers are tedious to maintain.

So what can we do other than using precompiled headers, merging all the cpp files into one, and keeping the number of headers minimal?

C++20 brings us preliminary support of modules! Eventually, you'll be able to forget about #include and the horrible compile performance that header files bring with them. Touched one file? Recompile only that file! Need to compile a fresh checkout? Compile in seconds rather than minutes and hours.

The C++ community should move to C++20 as soon as possible. C++ compiler developers should put more focus on this, C++ developers should start testing preliminary support in various compilers and use those compilers that support modules. This is the most important moment in C++ history!

Using dynamic linking instead of static one make you compiler faster that can feel.

If you use t Cmake, active the property:

set(BUILD_SHARED_LIBS ON)

Build Release, using static linking can get more optimize.

Related