I'm interning at Google for the summer, and the focus of my internship
is to improve C++ build times by caching the results of parsing
headers and persisting the results either on disk or in the memory of
a persistent compiler server process.
Pre-compiled headers already do this, but they generally only work for
projects that use the '#include "world.h"' header style. My goal is
to either improve the existing pre-compiled header support so that
this restriction can be lifted, or do something else entirely.
Clang's precompiled header support can be improved in many ways to support incremental compilation. However, I don't think the restriction requiring that the PCH include come first in the translation unit (the prefix header approach) can be lifted in any practical system that doesn't require the system headers to change. Per Bothner's paper, which you mentioned below, describes some of the problems with inclusion-order dependencies, but it doesn't even scratch the surface of ordering dependencies for C++, which has a large number of implicit dependencies. Even if we could manage to describe all of those dependencies---and I'm not certain we can---the computation required to do so is likely to kill any performance gains in the system.
As I see it, there are two approaches:
1) Live with this limitation, building the compilation server around the idea of implicitly creating a "world.h" (or multiple "world.h"'s) and sharing it among different translation units. One could extend the idea further with multi-level PCH files, where one PCH can reference another PCH, so that common prefixes of #includes could be shared more readily, and of course there are other opportunities for sharing (mmap'd file buffers, tokenized files, etc.)
2) Do something radically different, which reduces the number of dependencies between different headers and makes them more explicit. Something akin to the C++ "modules" proposals from Daveed Vandevoorde, for example.
Both are interesting, but they're very different. The first is a system that could be put into practice immediately, without changing system headers or libraries. The second is a much more ambitious long-term project that is likely to require changes to the C++ "world". Obviously, there are certainly other approaches that I'm not describing.
First and foremost, are people interested in this? I'm pretty sure
the answer is going to be yes, but I thought I should ask.
Yes, this is an interesting project and could reap big benefits for Clang.
Does anyone have any design advice? I'd like to hear from people who
worked on PCH about how to make this work. It requires doing things
like making sure all symbols that were not macros on the first include
are still not macros when the header is re-included, and some other
stuff with operator overloading that I'm not familiar with yet.
PCH already does simple validation of this kind. For example, say that macro X is defined when the PCH file is used. If X was also a macro definition in the PCH file, Clang will check that the definitions are identical; if X was not a macro definition, Clang will check that the identifier 'X' was not used anywhere in the PCH file.
Where should I do this development? Depending on the invasiveness of
the changes I'm making, I think it would be best to either work on
trunk or in a branch on the public svn repo.
Trunk is fine, so long as your work doesn't destabilize the Clang compiler itself.
There's also this previous work on GCC that was done by Per Bothner at Apple:
It's from 2003. I can't find my info about it on the web, so
presumably the effort was abandoned. I also got the impression that
the implementation was somewhat fragile from the undo buffers that
were used to deal with unwanted AST mutations during compilation. Can
anyone at Apple familiar with the effort share some advice that might
also apply to clang?
I am not familiar with that effort, but it has definitely been abandoned. I wouldn't attempt that approach again, because (as I said above) I don't believe it's practical to try to cope with all of the ordering dependencies in the current C/ObjC/C++ languages.
FWIW, 3x speedup (which Per Bothner's paper mentions) is what we're used to seeing for C and Objective-C projects that use Clang's PCH mechanism, so even if the compiler server were to "just" implicitly create prefix headers (as the first option I mentioned at the beginning), there are big wins available.