0

I'm currently speeding up compilation of a large C++ project (there is some C code too). Initially I'm

  • removing unnecessary system includes; and
  • introducing precompiled headers for common system includes such as stddef.h or vector. But not for includes like stdio.h or iostream which shouldn't be commonly used.

I'd thought that system headers would have include guards and thus would be covered by gcc's multiple include optimization. However it seems that not all headers follow this guideline. For example stdlib.h has #ifndef STDLIB_H at the start, and stddef.h follows the same pattern. But assert.h doesn't have a standard include guard, and nor does cstddef or cstdlib.

I've been using the -H option in gcc to track and subsequently analyse include dependencies in this project. What I've observed is that for a small sub-project with only 6 files, stdlib.h and stddef.h, which follow the include guards pattern, show up 6 times in the output, whereas the files that don't can show up 10s or 100s of times. I'm a little concerned that as some of these files may be on a remote network drive, compilation may be slower.

When I initially got precompiled headers working in the project, only including C++ headers such as vector, and headers from some internal libraries, I was a little surprised to only get a 20% performance increase. When I've done similar in Visual C++ I've seen greater increases. (Possibly related GCC build time doesn't benefit much from precompiled headers.)

I'm relatively new to gcc, so I may have missed something. My questions are:

  • Why do some headers have standard include guards, and some not?
  • Should I be concerned about the effect on compilation speed?
  • If so, how to address? I wouldn't mind adding assert to the precompiled headers, but I wouldn't want to add cstdlib, for example.
Community
  • 1
  • 1
TooTone
  • 7,129
  • 5
  • 34
  • 60
  • If they are internal they may not need them - as you should not include them directly – Ed Heal Jul 03 '15 at 17:33
  • 2
    PS - Consider forward declarations as well – Ed Heal Jul 03 '15 at 17:34
  • @EdHeal thanks, yes the internal ones do show up with `-H` (and some show up very many times in the output), but I'm primarily interested in headers like `assert`, `cassert` etc which I'm allowed include. Definitely agree re forward declarations: have been replacing `iostream` with `iosfwd` for example, and simply removing other includes that aren't used at all. – TooTone Jul 03 '15 at 17:37
  • 6
    `` cannot have include guards; its behaviour depends on whether `NDEBUG` is defined or not each time it is included. That is, a single translation unit (TU) could have `#include … #define NDEBUG … #include … #undef NDEBUG … #include ` and there could be different behaviour for the `assert` macro called in each set of code represented by `…`. This is a deliberate design requirement of the C standard (and presumably is required of `` in C++ too, though I've not gone and formally checked that). – Jonathan Leffler Jul 03 '15 at 17:37
  • 2
    Also consider using `#pragma once` for your header guards, especially if you think a lot of your compilation time is spent on fetching files from a network drive. The thing with header guards using `#ifndef` is that `#ifndef` serves many purposes and patterns, while `#pragma once` is only used for header guards. This means that the compiler can keep track of files that were included with `#pragma once` and completely ignore them when it sees them again, instead of opening the file then discarding after. – KABoissonneault Jul 03 '15 at 17:38
  • Also, search for [IWYU](https://github.com/include-what-you-use/include-what-you-use) (include what you use). It may help you. (It was on Google Code; that's closing down and it is now on GitHub.) – Jonathan Leffler Jul 03 '15 at 17:38
  • @KABoissonneault I can't edit system include files – TooTone Jul 03 '15 at 17:38
  • @TooTone Well I didn't really mean specifically for those files. If the system files are the only ones on a network, then fine. In that case, I don't see why you wouldn't want to put all the system files in the precompiled header. Precompiled headers are good for files that don't change often, and how often do your system headers change? – KABoissonneault Jul 03 '15 at 17:40
  • @JonathanLeffler thanks re `assert`, that makes sense and goes a long way to answering my first question. Do you think the same applies elsewhere? Looking at, e.g., `wchar.h`, the `__need...` constants are undefined after the include guards at the end of the file, in case `wchar.h` was already included. I guess the gcc guys are well aware of the advantages of having standard include guards but are prevented from doing so by C standards, legacy reasons etc? – TooTone Jul 03 '15 at 17:47
  • @KABoissonneault I prefer to only include files if and only if they are needed, because I like to limit dependencies through physically what files are allowed to see as well as e.g. using namespaces. For example in this project that were a number of uses of classes like `fstream` etc which were simply unnecessary and I'd prefer to have a situation where developers can use an `ostream` declaration say without adding an include (getting it via a forward declaration) but have the inconvenience of adding an include to get `fstream`. However I may have to reconsider my position. – TooTone Jul 03 '15 at 17:51
  • 1
    On the whole, I'd assume that the system headers are designed to minimize the compilation time to the extent that it can be. I'm not always 100% convinced, and GCC sometimes reports on system headers where it thinks header guards could be useful. I assume that there's a reason why they're not present when they're absent. That may be being too kind to the developers, but it also spares me from hacking the system headers — a fraught process that has to be reviewed after every upgrade, bug fix release, etc. Generally, leave the system headers alone (or report bugs in them, and wait for the fix). – Jonathan Leffler Jul 03 '15 at 17:56
  • @JonathanLeffler yes, with `-H` you do get "multiple include guard" warnings. And don't worry, I have no intention of hacking the include files :). Not that I'd be allowed to! – TooTone Jul 03 '15 at 17:59
  • 1
    If I had to hack a system header, I'd probably arrange to use a locally controlled header that primarily includes the system header. For example, you might designate `` as the header included by your project source code when you need `` which is missing include guards, etc. You can then do whatever shenanigans are necessary in your `` header to protect your code from the dubious practices of ``. Be careful to ensure that `` is used everywhere in your code. It's tougher with third-party libraries that use ``. – Jonathan Leffler Jul 03 '15 at 17:59

0 Answers0