Comparison of Header and Module Builds (WIP)
How do headers and modules compare when building an executable? Is one clearly better than the other?
Let’s look closely at the steps required to build a module and compare it with using headers. We’ll us the build to examine both processes. The name is long so it is reduced to just the initials in the diagrams.
Building with Headers
The compilation process using headers requires each header to be included in the source file which uses it. Diagrammatically it is:
1 abc.h main.cpp abc.h abc.cpp
2 │ │ │ │
3 └─────┬─────┘ └─────┬─────┘
4 v v
5 main.o abc.o
6 │ │
7 └────────────┬──────────┘
8 │
9 v
10 meow
Here the headerabc.h is used by both main.cpp and abc.cpp. The header file must be read and processed twice by the compiler. That is two compilations and one linkage to build meow.
Consider what happens with an incremental build when the header or the implementation changes.
- Header Change
-
A change to the header requires recompilation of both translation units and a link.
- Implementation Change
-
A change to the implementation file requires recompiling that file and relinking.
Building with Modules
Building an application using modules is more complex. Diagrammatically building the module ABC,cppm with an implementation file abc.cpp is:
1 Abc.cppm
2 ┌────┴───┐ abc.cpp
3 │ │ │
4 | v │
5 | Abc.BMI ─┬─────┘
6 │ │
7 V V
8 Abc.o ─┬────── abc.o
9 | main.cpp
10 │ │
11 │ Abc.BMI ─┬───┘
12 │ │
13 V V
14 abc.a ──┬───── main.o
15 │
16 V
17 meow
18
19 .BMI is ,gcm for g++ and .pcm for clang++
The first step is compiling the MIU Abc.cppm to produce the BMI and object files, which are then used in the subsequent steps. The BMI is used by abc.cpp to build its object file.
![]() |
In writing this section, I examined the CMake build process closely, which resolved a question about whether the module object files should be linked or archived. CMake creates an archive, as shown in the diagram. Archiving will be used in the later chapters. I’ll use the term linkage to refer to archiving or linking. However, I later found that Clang discourage using archives with modules. |
The two object files are linked to create an archive. Next, main.cpp is built using the BMI to generate an object file, which is linked with the archive to create the executable.
That is three compilations and two linkages to produce meow.
Again, let’s consider the steps for an incremental build if the MIU or the implementation changes.
- MIU Change:
-
A change to the MIU recompiles the MIU, does an archive build, and performs a link.
- Implementation Change
-
A change to the implementation recompiles the file, does an archive build, and performs a link.
Comparison Summary
It might seem like modules are worse off in terms of efficiency. The intent was to demonstrate using diagrams how the MIU, implementation files, and application files interact in a build, not directly comparing them with headers.
What is missing in the comparison?
- A header file library built with multiple implementation files would likely be archived, bringing it on par with modules in the number of linking steps.
- A change in a header file requires that every implementation file that uses the header be built. The build time becomes lengthy if an external header like the enormous
<regex>is included. - With modules, only implementation files that change their interface must be built along with the MIU. (They instigate the change to the MIU!)
In any non-trivial project, modules will be built more efficiently because they reduce the number of compilations. In fairness, the scanning process, as seen with the command-line builds of Aegean, does increase the compilation process.
