First thing, what are precompiled headers?
Once an entire dependency tree is exploded, a single c++ include file can become huge, and easily span tens if not hundreds of source files; these will need to be parsed for each compilation unit (c++ file), resulting in a large amount of duplicate work.
So compiler writers came up with the clever idea to optionally save an intermediate state of key headers, to reduce the amount of duplicate work. gcc , clang, msvc all support some variant of the precompiled headers idea.
How do they work in practice?i
Each compiler has its own quirks.
On GCC
A precompiled header has the same name as the header it accelerates with an additional .gch suffix, placed in the same directory as the header file it refers to. It is generated by calling the compiler with the same exact command line arguments as used to build the code, with the additional switches -x c++-header . If a precompiled header is present, it will be automatically used
On clang
A precompiled header has the same name as the header it accelerates with an additional .pch suffix. It is generated by calling the compiler with the same arguments as used to build the code, with the additional switches -x c++-header -emit-pch (the latter might be implicit if the former is supplied). To use it, it is not enough that it be present; the compiler switch -include-pch <pch-file-path> must be used.
On top of this: clang internal documentation highlights that there can only be one precompiled header and it must be included at the beginning of the translation unit
On MSVC
This is not yet a specific target for squid
Could it work for squid?
Yes, in theory. Our coding guidelines mandate that each c++ file start including “squid.h”, which in turn includes our whole portability abstraction layer, which in turn takes in several system files. On my Ubuntu Linux system, a total of 206 header files have to be read and parsed just for this purpose for each of the over 800 files that make up squid. Sounds promising!
Does it work for squid?
In short, unfortunately not. I have experimented with a feature branch, and the results are not what I was hoping for, under several dimensions.
The good: performance gains
I ran some checks, on a NUC6i7KYB (Intel(R) Core(TM) i7-6770HQ CPU @ 2.60GHz, with 16 GiB core, SSD). The test command was
git clean -fdx && ./bootstrap.sh && ./configure && time (make -s -j12 all && make -s -j12 check)
Over 6 attempts, wall clock time averaged 10 minutes and 18 seconds without precompiled headers, and 9 minutes and 48 seconds with them, so a roughly 30 seconds (or about 5%) compile time improvement with gcc. Good but not earth shaking.
The bad: poor integration with the autotools toolchain (gcc edition)
Autotools’ stance on precompiled headers is pretty clear:

This is how I’ve done it. It’s hacky, but some parts of it may not apply to other projects’ setup.
In configure.ac, define an user argument --enable-precompiled-headers , and react to it with an automake conditional ENABLE_PCH_GCC .
In src/Makefile.am , define a custom Makefile rule that builds the precompiled header:
$(top_srcdir)/include/squid.h.gch: $(top_srcdir)/include/squid.h
$(CXXCOMPILE) -x c++-header -o $@ $<
What’s wrong with this:
- src/Makefile.am is touching files in include/
This is necessary because include/ doesn’t have a Makefile.am of its own, and the top level Makefile.am doesn’t have access to the CXXCOMPILE variable. srcdirshouldn’t be mucked about at build time; that’s whatbuilddiris for
Then, add a section
if ENABLE_PCH_GCC
PCH_FILE=$(top_srcdir)/include/squid.h.gch
endif
which is then referenced in
BUILT_SOURCES = \
dnl ... \
$(PCH_FILE)
This will pull in the precompiled header in the list of dependencies of squid, unit tests, and files to clean up. We can’t really control the order this gets built in, but it isn’t a big deal: if we need to compile anything before the precompiled header is built, everything will still work, just without the speed bump.
The worse: poor integration with the autotools toolchain (clang edition)
Clang has one extra problem compared to gcc: to actually use the precompiled header, it needs the -include-pch <file> option. If the option is used, the file needs to be there, or the build will fail.
Which makes being unable to control the build order a showstopper. We would need to build the precompiled header file without that flag before we do anything else. But looking at the generated src/Makefile:
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-recursive
One way to make sure that we only add the –include-pch option would be to send it down the recursive make invocation, except we don’t control that.
That’s it, I give up
The benefit is just not worth the number of hacks and complexity.
What could make it work?
gcc gets this behaviour right; it would be great if clang took inspiration from them. At the very least, do not fail building if the included pch was missing. This would enable treating it for what it is: an optimisation.