You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
compile-many stage 2 re-compiles the framework per sketch — defeats stage-1 warmup, 2× slowdown vs single build on warm machine, ~3× on slow CI #335
FastLED PR #2661 first CI run (2-core runner, 108 uno examples): stage 2 reports 25s per sketch — vs ~1.5s for the equivalent single `fbuild build` in their legacy serial loop.
Root cause
`compile-many` stages each sketch in its own project dir (FastLED stages them under `.build/pio/uno/compile_many//`). Each stage-2 worker therefore gets its own per-sketch `.fbuild/build/uno/release/` and rebuilds the framework from scratch — confirmed by inspecting the per-sketch `compile_many.log`:
The design comment on `run_compile_many` (`crates/fbuild-cli/src/cli/compile_many.rs:75-79`) says:
Stage 1 builds the framework + library archives once (stage 1) and fans out per-sketch compile + link in parallel (stage 2). [...] one framework build.
But stage 1 writes its archive into stage 1's own project dir, which is invisible to stage 2's separate project dirs. So "one framework build" doesn't hold once the consumer stages sketches in their own dirs (the only way to parallelize them with distinct .ino content).
Compounding it: `run_stage2` in `crates/fbuild-build/src/compile_many.rs:519` hard-codes `jobs: 1` per worker, on the assumption that "per-sketch work is single-TU; framework archives are already pre-built." When that assumption is false (as for any per-sketch-dir staging), jobs=1 forces a serial framework re-compile in every stage-2 worker. On 2-core CI with 25 framework TUs at ~1s each that's the ~25s/sketch we measure.
Impact
For FastLED's nightly matrix (~80 board workflows × ~100 examples each), this is the difference between a 2-minute and a 7-minute board build, repeated across the whole matrix.
Suggested fix
Either:
Share the framework archive across stage-2 project dirs, e.g. resolve `(board, profile, framework_hash)` to a single `~/.fbuild/framework_cache//libframework.a` and have stage-2 orchestrators link against that path instead of re-running their own framework compile. Stage-1 populates the cache.
As an interim, drop the hard-coded `jobs: 1` in `run_stage2` and let stage-2 workers use `available_parallelism() / sketch_jobs` so each worker can at least compile framework files in parallel. Doesn't fix the framework re-compile, but turns the per-sketch wall time from "sum of TU times" back into "max of TU times".
I'd be happy to take a swing at (1) if you can confirm the design intent; (2) is a one-line change with a regression test.
Repro on a Linux 2-core box (or rate-limited cgroup) with the tip of `main`:
```bash
for n in s1 s2 s3 s4; do cp -r tests/platform/uno /tmp/sk/$n; done
time fbuild compile-many --board uno --sketch-jobs 2 --framework-jobs 2 /tmp/sk/s{1..4}
```
Expect "reused cached artifacts" on stage 2; observe "AVR build for uno completed" + matching "Compiled 25/25 files" in each per-sketch log.
Activity
ci: gate compile-many opt-in via FASTLED_USE_FBUILD_CI=1 (default off)
ci: gate compile-many opt-in via FASTLED_USE_FBUILD_CI=1 (default off)
fbuild: seed stage-2 project core/ from stage-1 to skip framework rec…
fbuild: seed stage-2 project core/ from stage-1 to skip framework rec…
fbuild: add real-toolchain stage-2 perf regression test (#342)
fix(build): anchor rebuild signatures to the compile workspace
fix(build): anchor rebuild signatures to the compile workspace (#1350)