Comeau C++ 4.x
Precompiled Headers

  1. 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. The Comeau front end 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.

  2. Automatic Precompiled Header Processing

    When --pch appears on the command line, automatic precompiled header processing is enabled. This means the front end 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 (see below) 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"
    #if TEST
    int i;

    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:

    1. The header stop point must appear at file scope - it may not be within an unclosed scope established by a header file. For example, a PCH file will not be created in this case:

      // xxx.h
      class A {
      // xxx.C
      #include "xxx.h"
      int i; }; 

    2. The header stop point may not be inside a declaration started within a header file, nor (in C++) may it be part of a declaration list of a linkage specification. For example, in the following case the header stop point is int, but since it is not the start of a new declaration, no PCH file will be created:

      // yyy.h
      // yyy.C
      #include "yyy.h"
      int i; 

    3. Similarly, the header stop point may not be inside a #if block or a #define started within a header file.

    4. The processing preceding the header stop must not have produced any errors. (Note: warnings and other diagnostics will not be reproduced when the PCH file is reused.)

    5. No references to predefined macros __DATE__ or __TIME__ may have appeared.

    6. No use of the #line preprocessing directive may have appeared.

    7. #pragma no_pch (see below) must not have appeared.

    8. The code preceding the header stop point must have introduced a sufficient number of declarations to justify the overhead associated with precompiled headers. By default, this is 1 in order to allow the user the most control (it can be changed under a custom porting arrangement).

    When the host system does not support memory mapping, so that everything to be saved in the precompiled header file is assigned to preallocated memory, two additional restrictions apply:

    1. The total memory needed at the header stop point cannot exceed the size of the block of preallocated memory.

    2. No single program entity saved can exceed the preallocation unit, a multiple of the host page size, often 64K, but sometimes 16K, for instance.

    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:

    1. The compiler version, including the date and time the compiler was built.

    2. The current directory (i.e., the directory in which the compilation is occurring).

    3. The command line options.

    4. The initial sequence of preprocessing directives from the primary source file, including #include directives.

    5. The date and time of the header files specified in #include directives.

    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 "

    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 (pch by default (this can be changed under a custom porting arrangement)). 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. The user 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:

    1. if the precompiled header file is based on at least one out-of-date header file but is otherwise applicable for the current compilation; or

    2. if the precompiled header file has the same base name as the source file being compiled (e.g., xxx.pch and xxx.C) but is not applicable for the current compilation (e.g., because of different command-line options).

    This handles some common cases; other PCH file clean-up must be dealt with by other means (e.g., by the user).

    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.

  3. 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.

  4. Other Ways for Users to Control Precompiled Headers

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

    1. #pragma hdrstop may be inserted in the primary source file at a point prior to the first token that does not belong to a preprocessing directive. It enables the user to specify where the set of header files subject to precompilation ends. For example,

      #include "xxx.h"
      #include "yyy.h"
      #pragma hdrstop
      #include "z.h" 

      Here, the precompiled header file will include processing state for xxx.h and yyy.h but not z.h. (This is useful if the user decides that the information added by what follows the #pragma hdrstop does not justify the creation of another PCH file.)

    2. #pragma no_pch may be used to suppress precompiled header processing for a given source file.

    3. Command-line option --pch_dir directory-name is used to specify the directory in which to search for and/or create a PCH file.

    Moreover, when the host system does not support memory mapping and preallocated memory is used instead ,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:

    1. Command-line option --pch_mem size may be used to change the size of the preallocated block of memory from its default value. The argument size is the number of 1024-byte units. (--pch_mem must also be among the first options on the command line.)

  5. Performance Issues

    The relative overhead incurred in writing out and reading back in a precompiled header file is quite small for reasonably large header files. (An implementation can adjust a threshhold under a custom porting arrangement to avoid creating precompiled header files with too little information in them.)

    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, users should expect to reorder the #include sections of their source files and/or to group #include directives within a commonly used header file. Note that this of course may compete with other source code organization strategies, hence this may be an engineering compromise you must decide.

    The Comeau front end source 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 ...

    pulls in another dozen header files, and a second, somewhat larger, PCH file is created. In all, the fifty-odd source files of the Comeau front end 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 Comeau front end.

    Different environments and different projects will have different needs, but in general, users 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.

    (c)© 1997-2013 Comeau Computing, EDG. All rights reserved.

    Comeau Computing
    91-34 120th Street
    Richmond Hill, NY 11418-3214

    Back to documentation Table of Contents
    /* the end */