I always knew precompiled headers mattered if you were using Visual C++. I didn’t know how much they really mattered.
What’s a precompiled header?
I’ve worked in a number of places. Where I am now is probably the biggest codebase I’ve worked on, well over a million lines of code with many projects in a large Visual C++ solution.
Most of the places I’ve worked have been C++ and Windows shops so I’m used to Visual C++ creating a pre-compiled header by default for each project with the usual Windows headers. I knew the Windows headers were huge so compiling them just once made sense. But I can’t remember anywhere I’ve worked where it was common to put much else in the pre-compiled header as it had drawbacks.
The two basic drawbacks of using pre-compiled headers are:
- if any file in the pre-compiled header did change it would trigger a recompile of everything
- it hid dependencies between files
So if you have your own standard utility header and include that in the stdafx.h (default pre-compiled header file) in all your projects then it only has to be compiled once for each project and not for every .cpp file so builds could be a little faster. But when this header does change it means the pre-compiled header has to be recompiled and that means every single .cpp file has to be recompiled.
Also if your header is in the stdafx.h you don’t need to put it in each .cpp file directly, so you probably don’t, and now it’s not visible that this .cpp file needs the standard utility functions in that header and dependencies in your code become harder to see and understand.
I (along with the many developers I’ve worked with) knew using the pre-compiled header mattered a bit so have been happy to let Visual C++ create one but thought it only mattered a little bit so never thought it worth adding anything else to it because of these drawbacks.
How much does it help?
Recently I watched "GoingNative 35: Fast Tips for Faster Builds!" from a series of videos by the Visual C++ team at Microsoft.
Our codebase is huge and our builds are slow (over 30mins for everything, 5 mins to rebuild many individual projects, and often several minutes in a local edit-build-test cycle). I’ve always assumed the build time was just due to the large codebase and that there wasn’t much we could do about it. So I thought the video might be interesting but didn’t expect anything startling. I was wrong.
In the video they talked about targeting build times in new versions of Visual C++ (VC2013 Update 3 and VC2015). And not just tiny improvements but 25%+ reduction in build times! Sounded great, but we can’t move to the latest version yet as too many other teams would need to move at the same time. This does give us an easy to explain reason why we want to take the time to upgrade. Unfortunately it’s often hard to explain the quality and productivity boost from new language features to business management but simple time savings from faster builds are easy to understand. This is very welcome, but that doesn’t help me today.
They also talked about pre-compiled headers. A long-standing feature that they suggested people were not taking full advantage of. They talked about 50% reduction of build time just from tuning the pre-compiled header! They’d even got an intern to write the Precompiled Header Refactoring Tool, an add-on to Visual C++ 2015 to recommend what to add to your pre-compiled header (using heuristics like all standard or 3rd-party headers, all headers used in over 50% of your .cpp files).
50% shorter compile times? That’s worth trying. That’s worth spending time to test. That’s enough to be worth occasionally causing a full recompile, that’s enough to get used to dependencies being hidden in stdafx.h.
So I tried it. And wow!
If you’re using Visual Studio 2015 I’d recommend you try the Precompiled Header Refactoring Tool as it’ll probably be faster. But it didn’t take long without it. I tried it on a few projects that see a lot of changes so a lot of recompiles. Simple test procedure:
- Time a rebuild of that project with your current stdafx.h (can also time with pre-compiled headers turned off just to make yourself feel better that you’ve been getting some benefit this whole time)
- Search for all #includes in project (and #using for C++/CLI)
- Remove duplicates
- Remove files you know are changed a lot, remove any files from this project itself
- Keep all std, boost, 3rd-party libraries
- Keep all local infrastructure headers that don’t change often like database and networking libraries
- Paste all these #includes into stdafx.h
- Re-time rebuild of that project
- Gasp as you see 50% and better improvements.
Of course it varies depending on your code. For me projects that took 5 minutes to build completely now take only 2 minutes. And even better a local edit-build-test cycle for individual changes improved from 2 minutes to under 1 minute, reducing the number of times I (and all my colleagues) start browsing the internet (or swordfighting) while waiting.
So I’m sold. It didn’t take long and sped up many builds significantly. I’ll definitely take a little time to at least put external headers into the pre-compiled header for any sizeable projects in future.
- GoingNative 35: Fast Tips for Faster Builds! https://channel9.msdn.com/Shows/C9-GoingNative/GoingNative-35-Fast-Tips-for-Faster-Builds
- Precompiled Header Refactoring Tool https://visualstudiogallery.msdn.microsoft.com/e6ca02bb-1c89-4ccc-bada-fb772cf122bd
- Swordfighting https://xkcd.com/303/