Skip to content

perf: build optimization (P0/P1/P2)#26

Merged
Sunrisepeak merged 4 commits into
mainfrom
build-optimization
May 11, 2026
Merged

perf: build optimization (P0/P1/P2)#26
Sunrisepeak merged 4 commits into
mainfrom
build-optimization

Conversation

@Sunrisepeak
Copy link
Copy Markdown
Member

Summary

Three build performance optimizations for incremental compilation:

  • P0: Frontend dirty check — skip prepare_build (toolchain resolve, scanner, make_plan) when build.ninja is newer than all source files + mcpp.toml. Reduces no-change builds from ~10s to <0.5s.

  • P1: Per-file dyndep — replace the global build.ninja.dd with per-file .dd files. Each .cppm gets its own .ddi → .dd conversion. Modifying one file no longer marks all 39 modules dirty.

  • P2: BMI copy_if_different — backup .gcm before compilation, restore old file if byte-identical. Combined with restat = 1, prevents cascade rebuilds when only implementation changes (not interface).

Test plan

  • mcpp build succeeds (full build)
  • mcpp test — all 86 unit tests pass
  • Per-file dyndep generates correct .dd files
  • BMI backup/restore logic doesn't break compilation

…nged

On a successful build, writes target/.build_cache with the output dir
and ninja binary path. On the next invocation, if build.ninja in that
dir is newer than all source files and mcpp.toml, invokes ninja directly
without re-running toolchain resolve, scanner, make_plan, or emit.

Reduces no-change builds from ~10s to <0.5s.
Replace the global build.ninja.dd (one dyndep file for all modules)
with per-file .dd files. Each .cppm gets its own .ddi → .dd conversion
via the new cxx_dyndep rule, and each compile edge references only its
own .dd file.

Before: touching one file → all 39 modules dirty → full rebuild (~21s)
After:  touching one file → only that file's .dd dirty → incremental

New `mcpp dyndep --single --output <file.dd> <file.ddi>` mode added
for the per-file conversion. Legacy multi-file mode still available.
GCC always updates the .gcm (BMI) file's timestamp even when the
module interface hasn't changed. This causes all downstream modules
to be recompiled unnecessarily.

Fix: the cxx_module rule now backs up the BMI before compilation,
and if the new BMI is byte-identical to the backup, restores the
old file (preserving its timestamp). Combined with restat = 1 in
the per-file dyndep entries, ninja skips downstream modules when
only the implementation changed.

This means modifying a function body without changing the module
interface no longer triggers a cascade rebuild of all importers.
@Sunrisepeak Sunrisepeak merged commit 7251ca7 into main May 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant