Increased build latency when multiple GCC processes share a precompiled header
0 reputation · 02 Nov 2022, 18:28 UTC
0 reputation · 02 Nov 2022, 18:28 UTC
Determine the root cause of the observable increase in compilation time when several GCC jobs simultaneously read the same precompiled header (.gch) file during a parallel build.
GCC treats the .gch file as read‑only for consuming compilations and does not implement internal locking or a concurrency‑safe mode; the documentation leaves any required synchronization to the surrounding build system. It is unclear whether the added latency originates from repeated regeneration attempts when the header is considered out‑of‑date, from file‑system contention on the shared .gch, or from incoherent caches that force some processes to fall back to source parsing.
What conditions trigger GCC to attempt regeneration of a shared .gch file during concurrent builds? How does GCC handle read‑only access versus write attempts when multiple processes detect an out‑of‑date header? Are there any compiler flags or build‑system practices that can mitigate this serialization without relying on external locking?
33275 reputation · 03 Nov 2022, 03:50 UTC
The latency is caused by concurrent regeneration attempts on the shared .gch. GCC decides whether a precompiled header is usable by checking an embedded command-line hash and the header's modification time. When either check fails, GCC attempts to rebuild the .gch itself. GCC provides no internal locking or concurrency-safe mode for .gch files, so every parallel job that considers the file stale starts its own rebuild. The final rename step is serialized by the filesystem, which creates a bottleneck; jobs that lose the race fall back to parsing the original header, adding wasted work and latency.
Confirmed, well-established behavior: GCC uses a hash and timestamp comparison to validate a .gch, and it writes a temporary file before renaming it over the target. The likely explanation for your build is that multiple jobs are detecting an out-of-date .gch at the same time. Whether that is happening in your case depends on whether the header or relevant compile options change during the parallel phase; that is the part that needs verification in your own build.
.gch once in a dedicated serial target before the parallel phase. For example:gcc -x c++-header -o common.h.gch common.hchmod a-w common.h.gch.gch. If the header later changes, your build system must repeat the serial regeneration step.-fpch-preprocess to the consuming compilations. This flag tells GCC to treat an existing .gch as read-only and to fall back to source parsing instead of rewriting it. Pair it with -Winvalid-pch so you receive a warning when the PCH is not actually used.-fpch-preprocess is generally available from GCC 4.7 onward; older releases may lack it. If you are on an older compiler, rely on the serial pre-step and read-only permissions alone.Create a minimal header and its .gch, then run two parallel jobs that include the header. Use strace -f -e trace=openat,rename to observe whether both processes attempt to open the .gch for writing and rename over it. After applying the mitigation, the trace should show read-only opens and no competing rename attempts. Also confirm that -Winvalid-pch stays silent, meaning the PCH is still being used.
Which GCC major version are you using? If it is older than 4.7, the -fpch-preprocess recommendation is unavailable and the build-system ordering plus file permissions become the only viable mitigation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.