This page grew from a discussion on comp.lang.c++.moderated about the most efficient way of #including header files in a C or C++ program. Normally, each header file should only be #included once. In a moderately sized project where header files #include other header files it can be difficult to keep to this rule without some form of automated help. A common idiom is to put "include guards" around the contents of the header file being included. E.g.
File "myheader.hpp"
#ifndef MYHEADER_HPP
#define MYHEADER_HPP
// Contents of include file go here
#endif
Thus, even if the file is #included multiple times, its contents will only be processed once. This is known as an internal include guard, because the guard is entirely internal to the header file.
One problem with this method is that if the compiler takes a naive approach, it will still have to open the file multiple times to check for the internal include guard. In a large project this could cause increased compile times. This point is made by John Lakos in his book "Large Scale C++ Software Design", and is used as an argument to support the following idiom:
File "myheader2.hpp"
#ifndef MYHEADER_HPP
#include "myheader.hpp"
#endif
// Rest of header file goes here
The guard symbol (MYHEADER_HPP) is still defined and checked internally, however by checking the guard symbol externally as well, the compiler might avoid having to open the header file at all. It is suggested that this external check is only done when a header file is included from other header files - when a file is #included from a source file the external check can be omitted. The internal guard is used to guarantee correctness, the external guard increases the speed of compilation. The cost involved is that of putting the check everywhere a header file is #included in another header file, and spelling the name of the guard symbol correctly.
It turns out that some compilers will allow us to have our cake and eat it by implementing the "include guard optimisation".
The include guard optimisation is when a compiler recognises the internal include guard idiom described above and takes steps to avoid opening the file multiple times. The compiler can look at an include file, strip out comments and white space and work out if the whole of the file is within the include guards. If it is, it stores the filename and include guard condition in a map. The next time the compiler is asked to include the file, it can check the include guard condition and make the decision whether to skip the file or #include it without needing to open the file.
Since not all compilers implement this optimisation I have put together a benchmark to allow the optimisation to be checked for. There is also a results page available to allow you to look up a given compiler.
The benchmark uses two header files - one with an internal include guard and one without:
guard.h | noguard.h |
---|---|
#ifndef GUARD_H #define GUARD_H extern int i; #endif |
extern int i; |
Three C source files then #include these header files multiple times:
#ifndef GUARD_H
#include "guard.h"
#endif
#ifndef GUARD_H
#include "guard.h"
#endif
#ifndef GUARD_H
#include "guard.h"
#endif
#ifndef GUARD_H
#include "guard.h"
#endif
.
.
.
This should compile quickly because the header file only has to be opened once.
#include "noguard.h"
#include "noguard.h"
#include "noguard.h"
#include "noguard.h"
.
.
.
This should compile slowly because the header file will have to be opened multiple times. Note that in practical code multiple inclusions of the same header file will almost certainly lead to violations of the one definition rule. This file is still valid and necessary for the benchmark though because it provides us with an "upper limit" timing - it tells us how long the compilation will take if the compiler is forced to open a header file multiple times.
#include "guard.h"
#include "guard.h"
#include "guard.h"
#include "guard.h"
.
.
.
If the internal include guard optimisation is implemented this will compile quickly because the header file will only have to be opened once (as in the external include guard case). If the optimisation is not implemented this will compile slowly, as in the no include guard case.
In order to get times that are large enough to measure easily, the number of includes needs to be large (several thousand). Sample test files and the software to generate them are available from the downloads section.
Download the software to run the test yourself
If you have any feedback on the benchmark or the contents of these pages please email me
This page last modified 12 August 2009