Skip to content

perf(cascade): measure the flight cascade — 1.2413 µs/tick, estimator is 91% (PERF-P01) - #361

Merged
avrabe merged 1 commit into
mainfrom
feat/perf-p01-cascade-baseline
Aug 25, 2026
Merged

perf(cascade): measure the flight cascade — 1.2413 µs/tick, estimator is 91% (PERF-P01)#361
avrabe merged 1 commit into
mainfrom
feat/perf-p01-cascade-baseline

Conversation

@avrabe

@avrabe avrabe commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Verify-Filter: (has-tag "performance")

Performance was the one axis on which this project could make no claim at all. engine-throughput has guarded the cFS engines since v1.56; the control path that actually flies had no timing baseline of any kind. The examples/*-bench binaries measure estimator accuracy, not time. A compositional WCET proof has existed in Lean and had never been confronted with a measurement.

Measured

cargo bench, Apple-silicon dev host, release profile:

stage median share
iekf_propagate 1.1256 µs 91.0 %
position_tick 73.40 ns 5.9 %
attitude_tick 15.47 ns 1.2 %
rate_tick 10.25 ns 0.8 %
mixer_mix 9.45 ns 0.8 %
full_cascade_tick 1.2413 µs per-tick control loop

CI spread < 0.1 % — stable, not noise.

Two findings

The estimator is the cascade. IEKF is 91 %; the other four together are ~108 ns. Any cascade performance work that isn't estimator work is rounding error.

Independently corroborated: jess's scry static stack bounds put iekf at 4192 B against 16–112 B for the others. Dynamic timing here, static analysis there, neither derived from the other, same conclusion about which stage carries the mass.

The 1 kHz budget is not close. 1.2413 µs against a 1 ms period is 0.12 % of budget, ~806× margin on this host. Even allowing an order of magnitude for a Cortex-M7 at a fraction of the clock, the control math will not bound the loop — the HAL, scheduler and sensor I/O will.

full_cascade_tick is the honest per-tick figure rather than a synthetic sum: the cascade is single-rate, cascade.step() runs every stage once per tick with no divider.

What this does NOT establish

The limits are the point, so they are in the artifact and the baseline file:

  • Not on-silicon. aarch64 host, warm cache, a branch predictor an M7 does not have.
  • Not a comparison. The PX4 head-to-head on the same board is criterion (d) and is not done. We can say what our cascade costs, not that it costs less than the alternative — and the second half is the claim worth having.
  • Not a tight SLA. No persisted history, so ceilings are coarse ~10× catastrophic guards — the same limitation FV-FALCON-PERF-001 records.

PERF-P01 and FV-FALCON-PERF-002 land at implemented, not verified. Criterion (d) is what would promote them.

One honesty note

The cargo bench step is deliberately not marked # bench-only. cargo bench is not in BENCH_PATTERNS, so that marker would claim something untrue — it genuinely runs in the gate (62 s), which is better evidence than a skip.

Evidence: gate (has-tag "performance") → 7 steps PASS, 0 FAIL. rivet validate PASS, exit 0.

Refs #8.

🤖 Generated with Claude Code

… is 91% (PERF-P01)

Performance was the ONE axis on which this project could make no claim at all.
`engine-throughput` has guarded the cFS engines since v1.56; the control path
that actually flies had no timing baseline of any kind. The examples/*-bench
binaries measure estimator ACCURACY, not time. A compositional WCET proof exists
in Lean and had never been confronted with a measurement.

MEASURED (cargo bench, Apple-silicon dev host, release, 2026-08-25):

  iekf_propagate      [1.1249  1.1256  1.1263] us   91.0 % of the cascade
  position_tick       [73.359  73.403  73.451] ns    5.9 %
  attitude_tick       [15.456  15.465  15.476] ns    1.2 %
  rate_tick           [10.245  10.252  10.261] ns    0.8 %
  mixer_mix           [ 9.4437  9.4491  9.4549] ns   0.8 %
  full_cascade_tick   [1.2406  1.2413  1.2421] us   <- per-tick control loop

CI spread < 0.1 % — stable measurements, not noise.

TWO FINDINGS.

(i) THE ESTIMATOR IS THE CASCADE. IEKF is 91 %; the other four together are
~108 ns. Any cascade performance work that is not estimator work is rounding
error. Independently corroborated: jess's scry STATIC stack bounds put iekf at
4192 B against 16-112 B for the others. Dynamic timing here, static analysis
there, neither derived from the other, same conclusion.

(ii) THE 1 kHz BUDGET IS NOT CLOSE. 1.2413 us against a 1 ms period is 0.12 %
of budget, ~806x margin on this host. Even allowing an order of magnitude for a
Cortex-M7 at a fraction of the clock, the control math will not bound the loop —
the HAL, scheduler and sensor I/O will.

`full_cascade_tick` is the honest per-tick figure, not a synthetic sum: the
cascade is single-rate, `cascade.step()` runs every stage once per tick with no
divider (wasm/cm/cascade/src/lib.rs).

WHAT THIS DOES NOT ESTABLISH, stated because the limits are the point:
  - not on-silicon (aarch64 host, warm cache, a branch predictor an M7 lacks)
  - not a comparison — the PX4 head-to-head on the same board is criterion (d)
    and is NOT done. We can say what our cascade costs, not that it costs less.
  - not a tight SLA — no persisted history, so ceilings are coarse ~10x
    catastrophic guards, same limitation FV-FALCON-PERF-001 records.

PERF-P01 and FV-FALCON-PERF-002 therefore land at `implemented`, not `verified`.
Criterion (d) is what would promote them, and nobody has taken that measurement.

Also: the `cargo bench` step is NOT marked bench-only, because `cargo bench` is
not in BENCH_PATTERNS and the marker would claim something untrue — it genuinely
runs in the gate (62s), which is better evidence than a skip.

  gate (has-tag "performance"): 7 steps PASS, 0 FAIL
  rivet validate: PASS, exit 0

Refs #8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
@avrabe
avrabe enabled auto-merge (squash) August 25, 2026 19:36
@avrabe
avrabe merged commit 207bb71 into main Aug 25, 2026
57 checks passed
@avrabe
avrabe deleted the feat/perf-p01-cascade-baseline branch August 25, 2026 20:05
avrabe added a commit that referenced this pull request Aug 26, 2026
…363)

v1.135 ships the cascade timing baseline and nothing else.

WHY: performance was the ONE axis on which this project could make no claim at
all. `engine-throughput` guarded the cFS engines since v1.56; the control path
that actually flies had no timing baseline of any kind, and the WCET proof in
Lean had never been confronted with a measurement. That was the highest-value
gap in the standing assessment, and PERF-P01 (#361) closes it:

  full_cascade_tick   1.2413 us    <- all five stages, one tick
    iekf_propagate    1.1256 us       91 % of the cascade
    the other four                    ~108 ns combined

Deferred out, each with the reason recorded in the artifact rather than moved
silently:

  TRACE-P02     -> v1.136   next, and now precisely sized
  MATHF32-P06   -> v1.139   not blocked, just lower value than the four ahead
  OCI-P03       -> v1.139   parked pending REWRITE, do not implement as written

TRACE-P02 is no longer one undifferentiated sweep. Measured 2026-08-25:

  155 of 236 verification artifacts have >=1 CI-runnable step
      -> promotable MECHANICALLY from the gate's own per-artifact result
   36 are bench-only        -> named waiver or a real step (judgement)
   45 have NO steps at all  -> cannot be evidence; write steps or demote

And ZERO `implemented` requirements lack a verifier — the 22/247 verified
figure is entirely UNPROMOTED verifiers, not missing evidence. That makes the
bulk of it far cheaper than it looked, and isolates the 45 as the real work.

OCI-P03's restated warning matters because it is easy to pick up by release
order: upstream wit-bindgen 0.60 already generates `[u8; 8]`, so the
fixed-length-list case for the fork is gone; the fork is three minors stale
(0.58 vs upstream 0.61.1) and NOTHING in relay depends on it. The drift that
actually matters is in the flight path — components pin wit-bindgen-rt 0.41
while the workspace is on 0.60, a 19-minor spread, and 0.41 generates the
bindings in every published artifact. Rewrite around that migration; it needs
no fork.

PERF-P01's criterion (d) — the PX4 head-to-head — is NOT discharged and is now
filed as #362 rather than left as an unmet criterion nobody re-reads. We can
say what our cascade costs; we cannot yet say it costs less.

rivet validate: PASS, exit 0.


Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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