Wednesday, November 30, 2011

Optimizing C++ Build Times

I've been fighting improving build times under C++ for years and over that time I've managed to put together a few good practices to help.

1) Use good header inclusion isolation.

Use forward declarations as much as possible.
Use the PIMPL idiom to provide better isolation.
Avoid include only libraries. Some are marginally OK, such as most of the STL. Others, especially certain boost libraries, can create enormous burdens. Wrap these using PIMPL or "has-a" instead of inheritance.
Use good encapsulation by splitting your classes into reasonably small DLLs. This reduces link times and helps enforce other good design practices.

In theory this should be all you need.

But there are some other things that work well.

2) Use IncrediBuild if you are building under Windows.

There are other distributed build systems as well, but at the moment I've not had any luck improving over local builds on today's multi-core hardware.

3) Use multi-core machines.

The more cores the better.

4) Use SSD drives to eliminate disk bottlenecks.

Depending on the amount of code you are compiling you may not need an SSD drive. However, in my current project I've seen a 30% improvement in build times.

I also have found that when using IncrediBuild in combination with an SSD can really improve the throughput allowing you to use many more machines. I've recently done tests where I ran up to 100 agents seeing significant improvements between 50 and 100.

5) Figure out what the hardware bottlenecks really are.

Memory speed. This is still a potential bottleneck in today's hardware.

Memory amount. This is easy, if you are using more than about 3/4 of your memory typically while building, get more.

Disk speed. The disk cache can get into a swamped state where it takes as much as several minutes to finish writing all the cached data. This can lead to randomly long link or compilation times. Use SSD drives to take care of this.

Network speed. If you are using distributed builds then go to at least a gigabit network. Beyond that I'm unsure that there would be any gain. Below that it is clearly a bottleneck.

CPU speed. Faster is better unless the bottleneck is memory speed, disk speed, or network speed.

Number of cores. Since compilation can be highly parallel more is better, unless memory speed or disk speed is an issue. Usually more cores means faster memory so it is normally just disk speed that can slow you down.

6) Turn off active virus scanning.

This should be obvious, but every .obj, .dll, or .exe that you create would get scanned thus slowing your compilations considerably.

I find it amazing how often this is overlooked.


Does this work? Yes. I've taken a build that was pushing 15 minutes and reduced it to about 8 using the methods in 1). I've take a build that was pushing 10 minutes and dropped it to about 7 using and SSD drive. I further cut the same build down to about 4 minutes by taking the number of IncrediBuild clients from 20 to 100, 5 minutes for 50 clients.

Slow builds are productivity suckers. The slowest build I ever saw was 30 hours. I only did that once. At one point I worked on a project that went from 5 minutes to 1 1/2 hours in year.

Somewhere not far past 5 minutes will put you into a state where flow gets interrupted. If you find your flow getting interrupted often, you will be far less productive. Don't let that happen.

Ideally you should strive to keep your builds under 1 minute. I believe this is possible for typical builds with the right encapsulation of data regardless of the total size of your project. While an entire system built from scratch could still take days, your normal builds should not need to take long. If they do, you are wasting resources.

No comments: