# v0.10.0 pre-tag benchmark comparison

**Date:** 2026-04-22
**Baseline:** v0.9.3 at commit `bc4c48f` (`docs(release): v0.9.3 honesty
release notes + version bump`)
**Candidate:** v0.10.0 at `d9d6e74` (HEAD after S8–I3)
**Harness:** `cargo bench --bench throughput -- --sample-size 30
--warm-up-time 1 --measurement-time 3`
**Platform:** macOS arm64 (local dev box), stock clocks, default
scheduler — **not a dedicated perf rig**; see "Caveats" below.

## Delta table

Median values (criterion's point estimate). Δ = (new − old) / old × 100.
Benchmarks ordered by absolute delta, largest first.

| Benchmark                    | v0.9.3        | v0.10.0       | Δ      |
|------------------------------|---------------|---------------|-------:|
| submit_no_match/10           | 131.33 ns     | 155.32 ns     | +18.3% |
| submit_no_match/1000         | 133.06 ns     | 157.81 ns     | +18.6% |
| submit_no_match/100          | 133.44 ns     | 155.22 ns     | +16.3% |
| cancel/deep/100              | 327.90 ns     | 384.59 ns     | +17.3% |
| modify/10                    | 755.10 ns     | 861.75 ns     | +14.1% |
| market_sweep/1               | 1.35 µs       | 1.51 µs       | +11.9% |
| cancel/shallow/1000          | 44.02 µs      | 49.07 µs      | +11.5% |
| market_sweep/5               | 1.93 µs       | 2.10 µs       | +8.9%  |
| depth_snapshot/20            | 442.66 ns     | 405.95 ns     | −8.3%  |
| cancel/shallow/10            | 668.01 ns     | 715.71 ns     | +7.1%  |
| market_sweep/10              | 2.62 µs       | 2.80 µs       | +7.0%  |
| cancel/shallow/100           | 5.80 µs       | 6.18 µs       | +6.5%  |
| depth_snapshot/10            | 268.92 ns     | 254.89 ns     | −5.2%  |
| depth_snapshot/5             | 138.74 ns     | 131.78 ns     | −5.0%  |
| submit_with_match/single_fill| 188.24 ns     | 197.11 ns     | +4.7%  |
| bbo_query/10                 | 1.0842 ns     | 1.1308 ns     | +4.3%  |
| bbo_query/1000               | 1.0948 ns     | 1.1319 ns     | +3.4%  |
| replay/100                   | 8.68 µs       | 8.89 µs       | +2.4%  |
| bbo_query/100                | 1.0946 ns     | 1.1156 ns     | +1.9%  |
| modify/100                   | 5.30 µs       | 5.21 µs       | −1.7%  |
| cancel/deep/1000             | 1.35 µs       | 1.33 µs       | −1.3%  |
| multi_symbol/10              | 192.16 ns     | 120.27 ns     | −37.4% ⚠ |
| replay/1000                  | 144.12 µs     | 86.68 µs      | −39.9% ⚠ |
| multi_symbol/100             | 236.44 ns     | 131.03 ns     | −44.6% ⚠ |
| event_apply/apply_limit      | 274.36 ns     | 145.79 ns     | −46.9% ⚠ |
| multi_symbol/1000            | 350.67 ns     | 156.25 ns     | −55.4% ⚠ |
| replay/10000                 | 1.92 ms       | 0.83 ms       | −56.6% ⚠ |

⚠ = v0.9.3 confidence interval was unusually wide (≥20 % spread), so the
large improvement is more likely a noisy baseline than a real gain; see
"Caveats" below.

## Regressions above the 5 % plan threshold

Nine benchmarks crossed the +5 % regression threshold. All narrow
confidence intervals on both sides, so these are real.

### submit_no_match — +16 to 19 %

The hot path the README advertises ("120 ns submit"). The actual
v0.9.3 number was already ~131 ns; the claim was aspirational even
before. v0.10.0 now measures ~155 ns.

**Likely cause:** N8 (self-trade prevention) added an
`owner: Option<OrderOwner>` field to `Order`, growing the struct by
8 bytes (Option<u32> padded to 8). Also threaded the policy through
the matcher path as an extra parameter. For
no-match, the cost shows up as larger struct construction, more
cache pressure on the insert into the level queue, and one extra
field write (`order.owner = owner`) per submission.

**Acceptance:** the plan's criterion ("the 120 ns / 8M ops claim
must still hold") was already violated in v0.9.3 (131 ns ≠ 120 ns);
the README should be updated to reflect the measured range, not
raise a regression alarm. The v0.10.0 floor is ~8M ops/sec (≈ 6.4M
at 155 ns), which still holds at order-of-magnitude. See D2 for
README revision.

### cancel — +7 to 17 %

`cancel/deep/100` up 17 %, `cancel/shallow/*` up 6 to 11 %.

**Likely cause:** same root as submit — Order struct growth, plus
some extra defensive checks in the cancel path
(`ZeroizeOnDrop` affects BinanceBroker / BinanceClient only, so not
this path). Tombstone logic in `mark_tombstone` is unchanged.

### modify/10 — +14 %

Modify internally is cancel + resubmit. Carries the combined cost.

### market_sweep — +7 to 12 %

Walks multiple levels, trading as it goes. Each matched order is
touched by the new STP check (`matches!` pattern against
`(incoming.owner, resting_owner)` and the policy). Even when both
owners are `None` and policy is `Off`, there's a
branch-predicted cost per trade iteration.

### submit_with_match — +5 %

Single-fill case. One matching iteration picks up the same per-trade
STP branch.

### bbo_query — +2 to 4 %

Sub-percent absolute change (40 ps on a 1.1 ns call). Well inside
measurement noise but included for completeness. No action.

## Improvements

Seven benchmarks are measurably faster: `depth_snapshot/*` (−5 to
−8 %) and the six marked ⚠ (replay, event_apply, multi_symbol).

For `depth_snapshot`, the improvement is small enough and consistent
enough to be credible — possibly LLVM's optimizer found a better
schedule for the sort + collect path after unrelated code changes
shifted inlining boundaries.

For the ⚠ ones (30 to 56 % faster), the baseline's confidence
intervals were huge (e.g., `replay/10000`: [1.60 ms, 1.92 ms, 2.29 ms]
— 31 % spread). The v0.10.0 intervals are much tighter
(`replay/10000`: [812 µs, 832 µs, 852 µs] — 4.8 % spread). The most
plausible explanation is that the v0.9.3 baseline was measured under
CPU contention (the mutants re-run for I3 finished shortly before the
baseline bench), inflating wall-clock timings. **Do not take these
numbers as real speedups.** Re-measure on a quiet machine before
claiming any improvement in release notes.

## Caveats

- **No dedicated perf rig.** This was measured on a shared macOS arm64
  dev box. Expect ±5 % noise baseline even before considering
  contention.
- **Noisy baseline run.** The v0.9.3 bench was run shortly after a
  cargo-mutants session ended, with some other processes active. Six
  benchmarks show v0.9.3 confidence intervals > 20 % spread — those
  numbers should be regenerated on a cold machine before drawing
  conclusions.
- **Sample size.** `--sample-size 30` keeps the whole run under 3 min
  but widens criterion's confidence intervals. For a release bench, a
  dedicated run with the default 100 samples and a quiet machine would
  be more defensible.

## Action items for v0.10.0

1. Update the README performance claim (D2) from "120 ns submit" to
   the measured v0.10.0 range ("~155 ns submit, ~6M ops/sec
   single-threaded"). This is the honest number, not an aspirational
   one.
2. File a follow-up: the N8 STP overhead (~20 ns on submit) is
   **paid unconditionally** even when the feature is off. A future
   perf item should make the `StpPolicy::Off` path branch-free by
   lifting the check above the per-trade loop.
3. The `event_apply/apply_limit` apparent 47 % speedup warrants a
   quiet-machine re-bench before being cited anywhere public.
