Our project just takes too long to compile. It’s only a few hundred thousand lines of code, but it compiles like there are millions.
I’ve spent most of last night and today reorganizing header files and project files and turning our primary include file set into a single, precompilable header.
Visual Studio, dramatic effect: just over 2 minutes 27 seconds off the time to build the entire project while also adding our network API to the build! Xcode seems about the same or slower (I’ll benchmark that next week) and I got tired of wrestling with autoconf/automake under Linux for the host.
Much of the slowness seems to be namespace wastage and header bloat: we have some, uhm, interesting conventions that have never quite gone away.
When our project started, C++ was still nascent and frowned upon, so C++ stuff was heavily avoided until I started pushing it about 5 years ago and it didn’t start infecting the client for about another year or so…
As a result, our early coders had a love for, what now seem ersatz, conventions like this:
typedef struct tagSTRUCTNAME
} STRUCTNAME, *LPSTRUCTNAME;
The tag bit is an ancient C throwback, the *LPSTRUCTNAME is a convenience hack which is apparently supposed to make pointers clearer and let you declare multiple pointers on a single line more easily:
STRUCTNAME *p1, *p2 ; // Normal style
LPSTRUCTNAME p1, p2 ; // LP style
But what it has the upshot of doing is meaning that every time we have a function-call prototypes header, it winds up #including the header with the type definitions. Instead of
extern void piGetSurface(…, struct piSURFACEDESC* surface, …) ;
#include “ldtex.h” // Include 3000 lines of headers
extern void piGetSurface(…, piLPSURFACEDESC surface, …) ;
This header file gets included in 21 cpp files and 9 header files. Only 2 of the CPP files actually care about the definition of piSURFACEDESC. The rest only need to know it’s some kind of structure that can be pointed to. 71 source files process the full 3000 lines of ldtex.h when all they need is one of the dozen or so function prototypes in a header file that includes it.
Additionally, every instance of this tripple-naming method of declaring a struct results in 3 entries in the namespace for one structure which definitely doesn’t help the compilers performance.
Then there are our copious named enums. There doesn’t seem to be a good way of doing a prototype with a named enum:
typedef enum PIZZA_TYPE =
extern void someFunction(PIZZA_TYPE, …) ;
You can’t do “extern void someFunction(enum PIZZA_TYPE, …)” in the forward declaration of someFunction, which means you have to include the enum declaration. And we have a lot of enum declarations. This significantly clutters up our namespace and header files. The “clean” way to do it would add hundreds, if not thousands, of new header files and create a maintenance nightmare.
(Before you suggest we not name our enum types, the point is to ensure strong typing)
Well, no matter. Significantly reducing the compile time of the Windows project is good. It probably gets the most builds and the host build time is already some 5 minutes faster than it was a few years ago. I’d have liked to have seen some improvements in XCode/GCC build times but I’ll come back to that in the future when I have time to fight the Gnu tools.
The meat of it was easy enough to set up; I wrote some scripts to remove the 3 main #include culprits (syscfg.h, platform.h and systypes.h) from all the header and CPP files, I reorganized the 3 headers into one platform-specific header and two common headers; then I used Visual Studio’s “forced include” / XCodes “prefix header” / GCC’s “-include” argument, and voila. Everything #include’s syscfg.h.
Then, under Visual Studio, I made syscfg.h the precompiled header for everything but the couple of .cpp files that need to not include it and voila.
I had tried, at first, doing
and making the platform’s project provide PN_SYSCFG_H=”folder/syscfg.h” but Windows didn’t like that. Oddly, if you do
#define PN_SYSCFG_H “folder/syscfg.h”
But if you define it thru the project settings instead, you get an error:
error C2006: ‘#include’ : “expected a filename, found ‘identifier’ defined file name” fatal error C1083: Cannot open include file: ”: No such file or directory
Infact, if you do both – the #define and the project setting – you get an error about redefinition of “PN_SYSCFG_H” citing “command line” as the previous definition, and then you get the C2006 error. Meh.