Precompiled Headers



We've seen program compilation times drop by up to a factor of 8 by using precompiled headers. To get the most benefit, you need to understand some tradeoffs, and how to organize your sources to get the most benefit. Precompiled headers work for the following KCC platforms:

Overview

The inclusion of many header files can slow down compilation because KCC has to reparse them for each compilation. The large header files in the (draft) ISO standard can be a particularly annoying source of overhead for small programs. Precompiled headers (PCH) greatly reduce the overhead.

To use precompiled headers:

On the first compile, KCC creates a precompiled header in directory /foo/bar. On subsequent recompiles, it tries to use the precompiled header. Here's an example program.
    #include 

    main() {
	std::cout << "Hello, world" << std::endl;
    }
The first time we compile it, KCC reports that it is creating a precompile header:
    $ KCC +K0 --pch --pch_dir /foo/bar hello.C 
    "hello.C": creating precompiled header file "/foo/bar/hello.pch"
The option +K0 was used to speed up compilation by turning off optimization. The next time we compile it, KCC reports that it is using the precompiled header:
    $ KCC +K0 --pch --pch_dir /foo/bar hello.C 
    "hello.C": using precompiled header file "/foo/bar/hello.pch"
The second time around, the compile is much faster (typically about 2x-4x for this example).

You may find that precompiled headers take up too much space. If so, read the detailed description below, which explains the tradeoffs of precompiled headers and how to reorganize your sources to reduce the disk overhead of precompiled headers.

Details of Precompiled Headers

It is often desirable to avoid recompiling a set of header files, especially when they introduce many lines of code and the primary source files that #include them are relatively small. KCC provides a mechanism for, in effect, taking a snapshot of the state of the compilation at a particular point and writing it to a disk file before completing the compilation; then, when recompiling the same source file or compiling another file with the same set of header files, it can recognize the ``snapshot point,'' verify that the corresponding precompiled header (``PCH'') file is reusable, and read it back in. Under the right circumstances, this can produce a dramatic improvement in compilation time; the trade-off is that PCH files can take a lot of disk space.

Automatic Precompiled Header Processing

When --pch appears on the command line, automatic precompiled header processing is enabled. This means that KCC will automatically look for a qualifying precompiled header file to read in and/or will create one for use on a subsequent compilation.

The PCH file will contain a snapshot of all the code preceding the ``header stop'' point. The header stop point is typically the first token in the primary source file that does not belong to a preprocessing directive, but it can also be specified directly by #pragma hdrstop if that comes first. For example:

    #include "xxx.h"
    #include "yyy.h"
    int i;
    
The header stop point is int (the first non-preprocessor token) and the PCH file will contain a snapshot reflecting the inclusion of xxx.h and yyy.h. If the first non-preprocessor token or the #pragma hdrstop appears within a #if block, the header stop point is the outermost enclosing #if. To illustrate, here's a more complicated example:
    #include "xxx.h"
    #ifndef YYY_H
    #define YYY_H 1
    #include "yyy.h"
    #endif
    #if TEST
    int i;
    #endif
Here, the first token that does not belong to a preprocessing directive is again int, but the header stop point is the start of the #if block containing it. The PCH file will reflect the inclusion of xxx.h and conditionally the definition of YYY_H and inclusion of yyy.h; it will not contain the state produced by #if TEST.

A PCH file will be produced only if the header stop point and the code preceding it (mainly, the header files themselves) meet certain requirements:

For Cray systems, two additional restrictions apply:

When a precompiled header file is produced, it contains, in addition to the snapshot of the compiler state, some information that can be checked to determine under what circumstances it can be reused. This includes:

This information comprises the PCH ``prefix.'' The prefix information of a given source file can be compared to the prefix information of a PCH file to determine whether the latter is applicable to the current compilation.

As an illustration, consider two source files:

    // a.C
    #include "xxx.h"
    ...                 // Start of code

    // b.C
    #include "xxx.h"
    ...                 // Start of code
When a.C is compiled with --pch, a precompiled header file named a.pch is created. Then, when b.C is compiled (or when a.C is recompiled), the prefix section of a.pch is read in for comparison with the current source file. If the command line options are identical, if xxx.h has not been modified, and so forth, then, instead of opening xxx.h and processing it line by line, the front end reads in the rest of a.pch and thereby establishes the state for the rest of the compilation.

It may be that more than one PCH file is applicable to a given compilation. If so, the largest (i.e., the one representing the most preprocessing directives from the primary source file) is used. For instance, Consider a primary source file that begins with

    #include "xxx.h"
    #include "yyy.h"
    #include "zzz.h"
If there is one PCH file for xxx.h and a second for xxx.h and yyy.h, the latter will be selected (assuming both are applicable to the current compilation). Moreover, after the PCH file for the first two headers is read in and the third is compiled, a new PCH file for all three headers may be created.

When a precompiled header file is created, it takes the name of the primary source file, with the suffix replaced by an implementation-specified suffix (see PCH_FILE_SUFFIX, which is set to pch by default). Unless --pch_dir is specified (see below), it is created in the directory of the primary source file.

When a precompiled header file is created or used, a message such as

    "test.C": creating precompiled header file "test.pch" 
is issued. You may suppress the message by using the command-line option --no_pch_messages. In automatic mode (i.e., when --pch is used) the front end will deem a precompiled header file obsolete and delete it under the following circumstances: This handles some common cases; other PCH file clean-up must be dealt with by other means (that means you).

Support for precompiled header processing is not available when multiple source files are specified in a single compilation: an error will be issued and the compilation aborted if the command line includes a request for precompiled header processing and specifies more than one primary source file.

Manual Precompiled Header Processing

Command-line option --create_pch file-name specifies that a precompiled header file of the specified name should be created.

Command-line option --use_pch file-name specifies that the indicated precompiled header file should be used for this compilation; if it is invalid (i.e., if its prefix does not match the prefix for the current primary source file), a warning will be issued and the PCH file will not be used.

When either of these options is used in conjunction with --pch_dir, the indicated file name (which may be a path name) is tacked on to the directory name, unless the file name is an absolute path name.

The --create_pch, --use_pch, and --pch options may not be used together. If more than one of these options is specified, only the last one will apply. Nevertheless, most of the description of automatic PCH processing applies to one or the other of these modes --- header stop points are determined the same way, PCH file applicability is determined the same way, and so forth.

Other Ways for Users to Control Precompiled Headers

There are several ways in which the you can control and/or tune how precompiled headers are created and used.

Moreover, when the host system is a Cray, then one of the command-line options --pch, --create_pch, or --use_pch, if it appears at all, must be the first option on the command line; and in addition:

Organizing Your Sources to Improve PCH Performance

The relative overhead incurred in writing out and reading back in a precompiled header file is quite small for reasonably large header files.

In general, it doesn't cost much to write a precompiled header file out even if it does not end up being used, and if it is used it almost always produces a significant speedup in compilation. The problem is that the precompiled header files can be quite large (from a minimum of about 250K bytes to several megabytes or more), and so one probably doesn't want many of them sitting around.

Thus, despite the faster recompilations, precompiled header processing is not likely to be justified for an arbitrary set of files with nonuniform initial sequences of preprocessing directives. Rather, the greatest benefit occurs when a number of source files can share the same PCH file. The more sharing, the less disk space is consumed. With sharing, the disadvantage of large precompiled header files can be minimized, without giving up the advantage of a significant speedup in compilation times.

Consequently, to take full advantage of header file precompilation, you should expect to reorder the #include sections of their source files and/or to group #include directives within a commonly used header file.

The source to KCC's parser provides an example of how this can be done. A common idiom is this:

    #include "fe_common.h"
    #pragma hdrstop
    #include ...
where fe_common.h pulls in, directly and indirectly, a few dozen header files; the #pragma hdrstop is inserted to get better sharing with fewer PCH files. The PCH file produced for fe_common.h is a bit over a megabyte in size. Another idiom, used by the source files involved in declaration processing, is this:
    #include "fe_common.h"
    #include "decl_hdrs.h"
    #pragma hdrstop
    #include ...
decl_hdrs.h pulls in another dozen header files, and a second, somewhat larger, PCH file is created. In all, the fifty-odd source files of the parser share just six precompiled header files. If disk space were at a premium, one could decide to make fe_common.h pull in all the header files used --- then, a single PCH file could be used in building the parser.

Different environments and different projects will have different needs, but in general, you should be aware that making the best use of the precompiled header support will require some experimentation and probably some minor changes to source code.


Next Section                 Copyright © 1996-1999. All rights reserved.

E-Mail KAI Technical Support    E-Mail KAI    Contact KAI   

This file last updated on 4 June 1999.