|
1 | 1 | # bench/fastled-examples |
2 | 2 |
|
3 | | -Warm-cache library-selection benchmarks across the FastLED examples matrix. |
4 | | -This is the harness referenced by `FastLED/fbuild#205` for acceptance |
5 | | -criterion **AC#5 / P-01**: |
6 | | - |
7 | | -> Warm library-selection on FastLED examples matrix `<= current fbuild |
8 | | -> + 50 ms`. |
9 | | -
|
10 | | -## Status: empty placeholder |
11 | | - |
12 | | -There is no harness in this directory yet — the real per-board, per-example |
13 | | -matrix needs a checked-out FastLED tree (`~/dev/fastled`) and orchestrator |
14 | | -wiring that routes through `resolve_cached`. That work is tracked |
15 | | -separately. The synthetic warm baseline (`MiniFramework`-backed cache hit, |
16 | | -no real FastLED) already exists at |
17 | | -[`../../crates/fbuild-library-select/benches/resolve_warm.rs`](../../crates/fbuild-library-select/benches/resolve_warm.rs) |
18 | | -and is the first-pass regression guard for the cache-hit path. |
19 | | - |
20 | | -Phase 4 K/V memoization itself shipped in PR #212, so the warm path is |
21 | | -real and measurable today; what is missing here is the multi-board, |
22 | | -real-sketch matrix that AC#5 requires. |
23 | | - |
24 | | -## The plan once the FastLED tree is wired in |
25 | | - |
26 | | -1. Iterate the FastLED examples tree (`~/dev/fastled/examples/**`) under |
27 | | - each supported board: at minimum `teensyLC`, `teensy30`, `teensy41`, |
28 | | - `stm32f103c8`, `esp32-s3`, `uno`, `ws2812`. The matrix expands with |
29 | | - board coverage. |
30 | | -2. For each `(example, board)` pair, run the resolver twice: |
31 | | - - **Cold pass.** Empty `~/.zccache/`. Captures the K/V miss path and |
32 | | - the underlying scan + walk + LDF cost. This is the P-02 lane. |
33 | | - - **Warm pass.** Populated `~/.zccache/`. Captures the K/V hit path, |
34 | | - where the only work should be cache lookup + result deserialization. |
35 | | - This is the P-01 lane. |
36 | | -3. Diff the warm scan time against a captured baseline checked into |
37 | | - `tasks/baseline-205.md`. CI fails the job if any `(example, board)` |
38 | | - regresses the warm path by more than 50 ms vs. that baseline (the |
39 | | - `#205` AC#5 threshold). |
40 | | -4. Emit a structured JSON report (`bench/fastled-examples/report.json`) |
41 | | - that future PR comments can diff. Format TBD with the harness. |
42 | | - |
43 | | -## Running the synthetic mini benches today |
44 | | - |
45 | | -The closest signal available right now without a FastLED checkout is the |
46 | | -per-crate cold and warm criterion benches against `MiniFramework`: |
| 3 | +Warm-cache library-selection benchmark across a curated FastLED examples |
| 4 | +matrix. This is the AC#5 / P-01 measurement for |
| 5 | +[`FastLED/fbuild#205`](https://github.com/FastLED/fbuild/issues/205). |
| 6 | + |
| 7 | +## What it measures |
| 8 | + |
| 9 | +For each example sketch under `$FASTLED_DIR/examples/`, runs the |
| 10 | +`fbuild_library_select::cache::resolve_cached` resolver twice against a |
| 11 | +fresh `KvStore`: |
| 12 | + |
| 13 | +- **Cold** — empty cache. Wall-clock includes the scanner walk over the |
| 14 | + FastLED `src/` tree (~1000 files), the 2-pass LDF reconciliation, and |
| 15 | + the cache write. This dominates total time. |
| 16 | +- **Warm** — cache pre-populated. Wall-clock includes the cache-key |
| 17 | + compute (sorted seed/header content hashing, bounded by `cache_key` |
| 18 | + itself) and the bincode decode of the cached `Selection`. |
| 19 | + `from_cache = true` is asserted so silent re-misses surface |
| 20 | + immediately. |
| 21 | + |
| 22 | +The framework library set is a synthetic Teensyduino-class stub built |
| 23 | +via `MiniFramework`. The bench measures resolver throughput, not the |
| 24 | +correctness of which libraries get selected — that is the acceptance-test |
| 25 | +layer (`crates/fbuild-build/tests/teensylc_acceptance.rs`). |
| 26 | + |
| 27 | +## Running |
| 28 | + |
| 29 | +`FASTLED_DIR` is required — there is no implicit default, since the |
| 30 | +correct path is host-dependent (CI uses `external/fastled` from the |
| 31 | +workflow checkout, developers use whatever convention they like) and a |
| 32 | +silent fallback would mask configuration mistakes. |
47 | 33 |
|
48 | 34 | ```bash |
49 | | -uv run soldr cargo bench -p fbuild-library-select --bench resolve_cold |
50 | | -uv run soldr cargo bench -p fbuild-library-select --bench resolve_warm |
| 35 | +FASTLED_DIR=/path/to/fastled \ |
| 36 | + uv run soldr cargo run --release -p fbuild-bench-fastled-examples |
| 37 | + |
| 38 | +# Emit a JSON report alongside stdout |
| 39 | +FASTLED_DIR=/path/to/fastled \ |
| 40 | + uv run soldr cargo run --release -p fbuild-bench-fastled-examples \ |
| 41 | + -- --json bench/fastled-examples/report.json |
51 | 42 | ``` |
52 | 43 |
|
53 | | -Those benches drive a synthetic ~30-library Teensyduino-class tree built |
54 | | -from `fbuild-test-support`'s `MiniFramework` rather than real FastLED |
55 | | -sketches. They are useful regression guards for the resolver and its |
56 | | -cache layer respectively, but they do **not** satisfy AC#5 on their own. |
| 44 | +If any example fails to measure (missing sketch, KvStore error, warm |
| 45 | +miss) the binary exits non-zero rather than skipping the row. CI must |
| 46 | +treat a partial matrix as a failure, not a pass. |
| 47 | + |
| 48 | +## Sample numbers |
| 49 | + |
| 50 | +Captured 2026-05-10 on Windows / Ryzen workstation, FastLED `main`, |
| 51 | +release build: |
| 52 | + |
| 53 | +| example | cold (ms) | warm (ms) | speedup | |
| 54 | +|---------------|----------:|----------:|--------:| |
| 55 | +| Blink | 923.58 | 11.36 | 81.3x | |
| 56 | +| Pacifica | 915.98 | 12.64 | 72.4x | |
| 57 | +| Animartrix | 970.14 | 11.76 | 82.5x | |
| 58 | +| Audio | 830.51 | 11.74 | 70.7x | |
| 59 | +| BlurBenchmark | 827.46 | 10.48 | 79.0x | |
| 60 | +| Chromancer | 844.13 | 10.89 | 77.5x | |
| 61 | + |
| 62 | +The warm path comfortably clears AC#5 (≤ +50 ms over current fbuild) at |
| 63 | +~11 ms per example. The ~75x speedup reflects the cost asymmetry between |
| 64 | +walking the FastLED `src/` tree (~1000 files) and a `KvStore` |
| 65 | +get + bincode decode of a serialized `Selection`. |
| 66 | + |
| 67 | +## Curated example set |
| 68 | + |
| 69 | +The harness intentionally runs a small representative subset rather than |
| 70 | +all 80+ examples. Adding more is cheap — see `EXAMPLES` in `src/main.rs`. |
| 71 | +The current set spans: |
| 72 | + |
| 73 | +- Trivial single-strip sketches (`Blink`) |
| 74 | +- Animation-heavy sketches (`Pacifica`, `Animartrix`) |
| 75 | +- I/O-heavy sketches (`Audio`) |
| 76 | +- Throughput stress sketches (`BlurBenchmark`, `Chromancer`) |
| 77 | + |
| 78 | +## CI |
| 79 | + |
| 80 | +The `fastled-examples` job in `.github/workflows/bench-205.yml` is |
| 81 | +`workflow_dispatch`-only because it requires a FastLED checkout. CI |
| 82 | +checks out FastLED at a pinned release tag (currently `3.10.3`) so |
| 83 | +measurements are reproducible, then runs the bench and uploads the JSON |
| 84 | +report as an artifact. Bumping the pin is a deliberate baseline event — |
| 85 | +update both the workflow `ref:` and the sample-numbers table above in |
| 86 | +lockstep. |
| 87 | + |
| 88 | +There is no automatic CI gate on the warm timings yet — first capture a |
| 89 | +stable cross-runner baseline, then a follow-up adds the threshold gate. |
57 | 90 |
|
58 | 91 | ## Cross-links |
59 | 92 |
|
60 | | -- Issue: `FastLED/fbuild#205` |
61 | | -- Phase 4 K/V memoization (shipped in #212): |
62 | | - [`../../tasks/zccache-kv-design.md`](../../tasks/zccache-kv-design.md) |
| 93 | +- Issue: [`FastLED/fbuild#205`](https://github.com/FastLED/fbuild/issues/205) |
| 94 | +- This harness: [`FastLED/fbuild#218`](https://github.com/FastLED/fbuild/issues/218) |
| 95 | +- Per-crate synthetic warm bench: |
| 96 | + [`crates/fbuild-library-select/benches/resolve_warm.rs`](../../crates/fbuild-library-select/benches/resolve_warm.rs) |
63 | 97 | - Subsystem architecture: |
64 | | - [`../../docs/architecture/library-selection.md`](../../docs/architecture/library-selection.md) |
65 | | -- Foundation baseline that the warm threshold compares against: |
66 | | - [`../../tasks/baseline-205.md`](../../tasks/baseline-205.md) |
67 | | -- Per-crate cold + warm benches (different scope, same subsystem): |
68 | | - [`../../crates/fbuild-library-select/benches/README.md`](../../crates/fbuild-library-select/benches/README.md), |
69 | | - [`../../crates/fbuild-header-scan/benches/README.md`](../../crates/fbuild-header-scan/benches/README.md) |
| 98 | + [`docs/architecture/library-selection.md`](../../docs/architecture/library-selection.md) |
0 commit comments