Those of you who have worked on middle-sized to large projects in C++ (any more than 50000 lines of code) know that build time becomes a rather important factor in the development process. On any typical project, a full build time can very well exceed 10 minutes on a modern machine. Although there are many factors aside from mere line count (for example, heavy use of templates) affecting the exact build time, any amount of time more than a few seconds you spend staring at your monitor waiting for a build to finish is hardly pleasant.
On really large projects, some very drastic measures are taken to mitigate the problem of build times. These may include distributed (even grid-based) build systems or dedicated build machines that automatically build the project after each code update to the project repository or at designated intervals. But for any smallish company or team, such measures may not be applicable. Using Pre-Compiled Headers (or PCH for short) can be an attractive solution.
First let me go over some basic stuff. As you know, a C++ compiler only accepts individual C++ code files as input (called a compiland (like operand!) sometimes.) Your project might easily have a couple of hundred (to a few thousand) source files (not counting the header files) but each must be fed through the compiler to produce an object file individually. The next step would be to run a linker (or alternately a “librarian”) to link those object files together and produce an executable (or a library) file.
We all know that all the “.cpp” (or “.cc” or whatever) files are compiled separately. The obvious result is that the header files that each source file includes must be read and parsed and incorporated into the object file once for each and every source file. This opens up an opportunity for greatly saving on compile time. You see, a typical source code file is usually between 200 to 2000 lines of code itself, but it very often includes tens of thousands of other code in form of included header files. It’s really easy to see for yourself. Just pass your source code file that includes a handful of STL headers to the compiler and tell it only to “preprocess” the code (check your compiler’s documentation.) Look at the resultant file. That’s what the poor compiler receives for each cimpiland.
If you use template classes and functions heavily, the situation becomes even more difficult. Because each template class will be specialized by the compiler for each usage throughout the code. That is, you write a 500-line template class, and use it with 20 different types as template arguments. That’s 10000 lines of code right there, not to mention the fact that the source is now much more complex for the compiler to optimize and generate target code from. Let’s face it, templates are great facilities for generic programming, cleaner design, better performance and general wizardry, but they’re no friend of the compiler’s!
Anyway, suppose that you include <map>, <fstream> and <algorithm> headers (from STL) in almost all of your source code files. Since each file is passed to a separate instance of the compiler, no information is interchanged between the instances and the compiler reads, parses, generates code from and optimizes all the three header files for each source file. This is clearly superfluous. The opportunity I mentioned is right here. If you could somehow do the many steps necessary for processing these files (or at least some of the steps) and store the results and the state of the compiler somewhere (in a file, for example) you wouldn’t have to do that for every source file. You would just read that intermediate state file almost directly into memory and save a lot of time.
This is precisely what using pre-compiled headers is. There are some restrictions however. The headers you want to compile into a PCH file have to be exactly the same and they have to be included in every source file and in exactly the same order. This might mean that some of your source files may include headers that they don’t actually need, but this seldom causes any problem. Also, it is essential that the headers you want to pre-compile be included as exactly the first statements of each source file. Because C++ code (specially preprocessor code) can affect the way other included header files (that are included further in the file) behave, there must not be any code before the inclusion of the headers intended for pre-compilation or that code too, becomes part of the PCH.
One more thing that is necessary is to tell the compiler to stop putting headers into the PCH at some point. This is achieved by putting a “#pragma hdrstop” after all the headers that we intend to be included in the PCH file, and before anything else.
There’s a convenient way to manage all this. We can put all the inclusion statements of all the include files we want to go into the PCH file, along with the pragma directive in a specifically designated header file (let’s call it “PCH.h“) and include this file at the very top of each and every source file (remember: only source files, not other header files.) This ensures that the header files and their order is exactly consistent across all files and removes the need for much duplication of #include statements that might lead to much grief. (As a side note: Don’t Repeat Yourself!)
Now, you tell the compiler to create the PCH, and use it throughout the build. Therefore, you have to change the compile command for exactly one of your source files to create the PCH while it’s being compiled (and put this command at the beginning of the list of files to be compiled in your Makefile or whatever) and change the compile command of all the other source files to only use this newly created PCH file.
Since the precompiled header can become a very big file (many tens or even hundreds of mebi-bytes) and it has to be rebuilt every time either the headers included in it are changed (a genuine requirement) or the source file used to create it changes (imposed, because the code in that file will not be included in the PCH anyway) we can create a dedicated source file just for the purpose of PCH creation. This (e.g.) “PCH.cpp file will be empty except for a single #include <PCH.h> statement. This way, this source file never changes and one cause (however minor) for rebuilding of PCH is eliminated.
For those of you who use IDEs and Microsoft Visual Studio in particular (like myself) here’s how you do it in the VS IDE. First go on and create the said “PCH.h” and “PCH.cpp” files above. Remember to include “PCH.h” in every source file at the very top and remember to add “PCH.cpp” to your project. Also very important is that you must remove all the inclusion statements of the files already included in the PCH from each and every source and header file in your project (it’s best to #define them out (actually #if defined them out,) in case this didn’t work out for you and you wanted them back!
OK. Now you have to tell VS to tell VC that you’re going to use PCH. Go to Project->Properties and in that dialog (while making sure “All Configurations” is selected in the “Configuration” combo box on the top left) Configuration Properties->C/C++->Precompiled Headers. Change the value of “Create/Use Precompiled Header” to “Use…” (with command-line switch of “/Yu”) and change the file name in “Create/Use PCH Through File” to whatever the name of your header is (”PCH.h” in our example) and don’t forget any path prefix it might have (you shouldn’t give an absolute path here, but a path relative to one of your include search path directories or your source file directory.)
This has the effect of setting the compile commands for all your source files to use PCH. But someone has to create it first! Find (in “Solution Explorer” window,) the source file you designated to create the PCH (for example “PCH.cpp”) and (right-click on it and) open it’s “Properties” dialog. While again making sure “All Configurations” is selected (as opposed to just “Debug” or “Release” or whatever), find your way to the same place as before (PCH settings.) These are the build options just for our selected file. Only change the “Create/Use Precompiled Header” to “Create…” (instead of “Use…”) and your all set. You should not change the name of the files (neither the header file nor the resulting PCH file) in this dialog.
Now make a full rebuild of the project. If you’ve done it right, and I haven’t forgotten anything important, and your project is big enough, with this rebuild you should see a great speed up. I have seen build times go from 20-25 minutes to 5 minutes and from 8-9 minutes to 1:50-2 minutes instantly.
I suggest you time your builds before and after the use of PCH (in VS2005, go to Tools, Options…, Projects and Solutions, VC++ Project Settings, Build Timing and set it to “Yes”.)
One thing to note. Sometimes, if your project is large enough, the VC compiler will run out of memory and terminate while building the PCH. In that case, you should append a “/ZmXXX” to all your compile commands, where XXX is the amount of memory in some weird scale and unit. Check the compiler documentation (or follow the build error you get when you run into this problem.) In order to do this inside the IDE, go to you project properties dialog, make sure “All Configurations” is selected, go to “C/C++”, “Command Line” and add the (e.g.) “Zm150″ to the (probably) empty box of “Additional Options”. Experiment with the value a bit to find a suitable value that doesn’t exhaust all your available memory and still allows a build.
I hope this mini-guide comes handy to someone someday! Please let me know of any errors I’ve made, or any omissions or inaccuracies.