Version: mcpp 2026.8.28.2, llvm@22.1.8, x86_64-linux-gnu, 8 cores.
On a workspace where every target is already built and cached, mcpp test -p <member>
still spends most of its wall time in a single silent phase before test compilation
starts. Nothing is recompiled — the phase produces no artefacts at all.
For our workspace the full test sweep costs ~17 minutes, and this phase is nearly all
of it. mcpp build over the same workspace, fully cached, finishes in 0.7s.
What the time is spent on
Timestamping each output line of a repeat run (member and test names elided):
0.00s Workspace building member '<member>'
0.09s Resolving toolchain
0.09s Resolved llvm@22.1.8 → .../bin/clang++
0.13s Target x86_64-unknown-linux-gnu
0.21s Compiling <member> v0.1.0 (.)
0.21s Cached <dep-a> v0.2.3 (7 units)
0.21s Cached <dep-b> v3.12.0 (1 unit)
15.84s Compiling <test-1> (test) ← 15.6s of silence
15.84s Compiling <test-2> (test)
... 7 more, all at 15.84s
15.86s <test-2> ... ok (0.02s) ← every binary already up to date
15.90s test result ok. 9 passed; 0 failed; finished in 15.92s (build 15.63s + run 0.06s)
Three observations about that 15.6s window:
- Nothing is written. Touching a marker file and then listing everything newer
than it under the member's target/ and under $MCPP_HOME/build-cache yields
5 .json files and 1 .ninja file. No .pcm, no .o, no executables.
- No child processes. Sampling
ps --ppid <mcpp-pid> every 1.5s returns nothing
for the whole window, and /proc/<pid>/wchan reads 0 throughout — mcpp is
running on-CPU in-process. Neither clang nor ninja is spawned.
- It is per-invocation, not a cold cache. Running the same command twice in a row
gives the same time both times, and interleaving other members does not change it.
How it scales
Not with the number of cached dependency units — with the module-interface surface of
the whole dependency closure, including workspace path dependencies (which are not
counted in the (N units) line):
| member |
cached dep units |
build phase |
| A — no dependencies |
0 |
3.3s |
| B — two small index deps |
8 |
15.7s |
| C — one vendored dep, 73 units |
73 |
60s (133s on a colder run) |
D — same 8 index units as B, plus a path dep on a large member |
8 |
154s |
D is the interesting row: same cached-unit count as B, ~10x the time, and the only
difference is a path dependency on a member with a few hundred module interfaces.
C measured 60s and 133s for the identical command on different runs, which looks like
page-cache sensitivity — consistent with the phase reading a lot of files.
Things that don't help
| attempt |
result |
--profile dev (test defaults to release, build defaults to dev) |
identical, 15.5s |
--cache local |
43s — 3x worse |
| repeating the command / reordering members |
no effect |
Why it matters
It puts a floor on the edit-test loop that is independent of how small the edit was.
For us it is the difference between a 7-minute and a 17-minute verification pass, and
it scales with workspace size rather than with change size, so it gets worse over time.
If the planning result were memoised — keyed on the resolved manifest set and the
toolchain, invalidated when either changes — a warm repeat invocation could skip it
entirely, the same way mcpp build already does.
Happy to put together a minimal reproducing workspace (a member with a large path
dependency seems to be enough to show it) if that would help.
Version: mcpp 2026.8.28.2, llvm@22.1.8, x86_64-linux-gnu, 8 cores.
On a workspace where every target is already built and cached,
mcpp test -p <member>still spends most of its wall time in a single silent phase before test compilation
starts. Nothing is recompiled — the phase produces no artefacts at all.
For our workspace the full test sweep costs ~17 minutes, and this phase is nearly all
of it.
mcpp buildover the same workspace, fully cached, finishes in 0.7s.What the time is spent on
Timestamping each output line of a repeat run (member and test names elided):
Three observations about that 15.6s window:
than it under the member's
target/and under$MCPP_HOME/build-cacheyields5
.jsonfiles and 1.ninjafile. No.pcm, no.o, no executables.ps --ppid <mcpp-pid>every 1.5s returns nothingfor the whole window, and
/proc/<pid>/wchanreads0throughout — mcpp isrunning on-CPU in-process. Neither clang nor ninja is spawned.
gives the same time both times, and interleaving other members does not change it.
How it scales
Not with the number of cached dependency units — with the module-interface surface of
the whole dependency closure, including workspace
pathdependencies (which are notcounted in the
(N units)line):pathdep on a large memberD is the interesting row: same cached-unit count as B, ~10x the time, and the only
difference is a
pathdependency on a member with a few hundred module interfaces.C measured 60s and 133s for the identical command on different runs, which looks like
page-cache sensitivity — consistent with the phase reading a lot of files.
Things that don't help
--profile dev(testdefaults torelease,builddefaults todev)--cache localWhy it matters
It puts a floor on the edit-test loop that is independent of how small the edit was.
For us it is the difference between a 7-minute and a 17-minute verification pass, and
it scales with workspace size rather than with change size, so it gets worse over time.
If the planning result were memoised — keyed on the resolved manifest set and the
toolchain, invalidated when either changes — a warm repeat invocation could skip it
entirely, the same way
mcpp buildalready does.Happy to put together a minimal reproducing workspace (a member with a large
pathdependency seems to be enough to show it) if that would help.