|
| 1 | +# Alpha.10 performance admission review (issue #1225) |
| 2 | + |
| 3 | +This is the bounded structural-regression adjudication required by issue #1225 |
| 4 | +(B1.6, stage #1150, umbrella #1155, authority ADR-0151). It reviews the |
| 5 | +Alpha.10 diagnostic benchmark baseline established by A10.11 / #1219 for |
| 6 | +structural contradictions with the architecture only. It is an adjudication |
| 7 | +record, not a ranking claim and not a performance SLO. Rank optimization and |
| 8 | +performance tuning are explicit non-goals of #1225. |
| 9 | + |
| 10 | +## Reviewed baseline |
| 11 | + |
| 12 | +- JFB browser baseline: `benchmarks/jfb/evidence.json` (schemaVersion 1, |
| 13 | + recordedAt 2026-09-02T17:34:29Z) |
| 14 | +- Kernel/Region/claim/compiler microbenchmarks: |
| 15 | + `benchmarks/v044/micro-evidence.json` (recordedAt 2026-09-02T17:31:52Z) |
| 16 | +- alpha.7 qualification matrix: `benchmarks/v044/evidence.json` |
| 17 | +- Narrative record: `docs/current/v0.44.0-PERFORMANCE.md` |
| 18 | +- Harness self-review: `benchmarks/jfb/README.md` |
| 19 | + |
| 20 | +Baseline provenance: OpenElement SHA |
| 21 | +`493548a6b30b5f2912550c8dfaac70a8b9ef153f`; JFB commit |
| 22 | +`21d7204da754846fe1402f4437b5b53066f3c34e` with per-file SHA-256-pinned stock |
| 23 | +comparators (vanillajs, preact-signals, lit, solid, vue, svelte) built |
| 24 | +unmodified; Chromium 147.0.7727.15 via Playwright 1.59.1; stock afterframe |
| 25 | +timing and stock warmup counts; 10 CPU iterations per benchmark (stock 15, |
| 26 | +recorded deviation); Apple M2, 8 cores, 16 GB, macOS arm64; Deno 2.9.0, Node |
| 27 | +v24.18.0. Every number cited below was re-extracted from the committed |
| 28 | +evidence JSONs during this review and matches the narrative record. |
| 29 | + |
| 30 | +## Evidence-SHA gap re-confirmation (carried risk, re-confirmed not re-litigated) |
| 31 | + |
| 32 | +The benchmark evidence SHA `493548a6` predates this review's base SHA |
| 33 | +`17ec6a57`. Re-confirmed at `17ec6a57`: |
| 34 | + |
| 35 | +- `git diff --stat 493548a6..17ec6a57 -- packages/element/src |
| 36 | + packages/adapter-vite/src packages/app/src packages/ui/src |
| 37 | + packages/create/src` → empty (zero product-source changes). |
| 38 | +- The intervening commits (#1259 baseline itself, #1261 docs truth, #1262 |
| 39 | + docs/examples/www vocabulary, #1263 closure evidence and verifier tests, |
| 40 | + #1264/#1265 governance docs, #1266 package-surface freeze gate and docs) |
| 41 | + touch documentation, governance, examples, gates and tests only. |
| 42 | + |
| 43 | +The measured semantics at `493548a6` are therefore the semantics at |
| 44 | +`17ec6a57`; the gap is docs/harness-only, as the Alpha.10 verifier already |
| 45 | +verified. |
| 46 | + |
| 47 | +## Reproducibility spot-check at the review SHA |
| 48 | + |
| 49 | +- `deno test -A benchmarks/` at `17ec6a57` → exit 0, 12 passed, 0 failed. |
| 50 | + These are the deterministic harness self-checks (`benchmarks/jfb/harness.test.ts`, |
| 51 | + `benchmarks/v044/micro.test.ts`, `benchmarks/v044/performance.test.ts`) that |
| 52 | + assert exact DOM-op counts, evidence schema and the frozen alpha budgets — |
| 53 | + never timings. The harness is intact; no harness fix was needed. |
| 54 | + |
| 55 | +A full JFB browser re-run was not repeated: it rebuilds pinned stock |
| 56 | +comparators out-of-tree and would rewrite the committed evidence file, which |
| 57 | +this packet does not own. The deterministic count assertions plus the |
| 58 | +committed raw samples carry the reproducibility contract. |
| 59 | + |
| 60 | +## Verdicts |
| 61 | + |
| 62 | +Verdict scale per #1225: CLEAR (no structural regression, numbers cited) / |
| 63 | +ACCEPTED TRADE-OFF (documented with evidence) / BLOCKER (structural |
| 64 | +regression; stop and report). |
| 65 | + |
| 66 | +### 1. Exact Part update catastrophically slower than comparable fine-grained systems — CLEAR |
| 67 | + |
| 68 | +Browser (JFB `03_update10th`, partial update of every 10th row in 1k, median |
| 69 | +ms): OE 15.55 vs vanillajs 19.35, lit 23.95, solid 18.35, svelte 24.95 — |
| 70 | +OE is faster than four of six comparators including vanilla; preact-signals |
| 71 | +10.5 and vue 11.6 are faster than OE. No catastrophic gap in either |
| 72 | +direction. |
| 73 | + |
| 74 | +Micro (fake DOM, exact op counts from `micro-evidence.json`): |
| 75 | + |
| 76 | +- partial update of every 10th of 1k rows: exactly 100 text writes, 0 |
| 77 | + allocations, 0 listener adds, 1.573 ms — one write per affected row, no |
| 78 | + more; |
| 79 | +- signal → text part: 0.448 µs/op, exactly 2000/2000 text writes; |
| 80 | +- signal → attr part: 0.265 µs/op, 2000/2000 attr writes; |
| 81 | +- signal → prop part: 1.309 µs/op, 1999/2000 value writes (equality guard, |
| 82 | + explained); |
| 83 | +- engine floor (signal write, no part): 0.096 µs/op. |
| 84 | + |
| 85 | +Per-update Part overhead over the raw signal-write floor is 0.17–1.2 µs and |
| 86 | +is fully explained by the op counts: the exact Part update path writes only |
| 87 | +the affected Part. No structural regression. |
| 88 | + |
| 89 | +### 2. Keyed Region pathological complexity — ACCEPTED TRADE-OFF |
| 90 | + |
| 91 | +The one outlier in the baseline: keyed `swap1k` performs 997 node moves |
| 92 | +(insertions) for a 2-row swap — the Region move is O(distance) |
| 93 | +node-by-node. Browser median 19.85 ms vs solid 7.85, preact-signals 9.55, |
| 94 | +vanillajs 12.8; but vue 19.7, lit 21.25 and svelte 22.7 are equal or worse. |
| 95 | + |
| 96 | +Why this is a trade-off, not a blocker: |
| 97 | + |
| 98 | +- The complexity is linear in move distance, not superlinear; there is no |
| 99 | + unbounded or quadratic path anywhere in the Region evidence. |
| 100 | +- Every other keyed Region op is mid-pack or better (median ms): replace1k |
| 101 | + 27.35 (lit 282.6), remove1k 11.15 (lit 21.95, svelte 22.5), clear1k 4.15 |
| 102 | + (lit 330, preact-signals 12.5), append1k 41.95 (mid-pack), create10k |
| 103 | + 253.45 (preact-signals 287.4, lit 268.15 slower). CPU geomean 22.728 sits |
| 104 | + in the same band as vue 22.484 and svelte 22.195. |
| 105 | +- The finding was recorded in the baseline itself (#1219 closure comment, |
| 106 | + `v0.44.0-PERFORMANCE.md` diagnostic finding 1) as an algorithmic |
| 107 | + inefficiency, not a correctness break. |
| 108 | + |
| 109 | +Disposition: the O(distance) move algorithm is accepted for Beta.1 admission |
| 110 | +because it is correct, linear, bounded and within the mainstream-framework |
| 111 | +band on its worst op. Move-algorithm optimization is a stated non-goal of |
| 112 | +#1225 and remains deferred to the evidence-gated B3.4 performance work. |
| 113 | + |
| 114 | +### 3. Memory/listener/subscription growth (leaks) — CLEAR |
| 115 | + |
| 116 | +Retention evidence: |
| 117 | + |
| 118 | +- Micro churn: 25 cycles × 200 rows → 0 retained subscriptions, 0 retained |
| 119 | + listeners (`micro-evidence.json` stability section). |
| 120 | +- alpha.7 qualification: 100 churn cycles per scenario across all four |
| 121 | + scenarios → 0 retained subscriptions, 0 retained listeners |
| 122 | + (`benchmarks/v044/evidence.json`); the acceptance gate fails closed on any |
| 123 | + retention (`docs/current/v0.44.0-PERFORMANCE.md` acceptance contract). |
| 124 | +- Browser heap after run1k + clear ×5 with forced GC: 1,400,756 bytes vs |
| 125 | + 1,219,651 ready (+14.8%) — memory returns to near-ready after clear, so |
| 126 | + there is no cumulative growth. Vanillajs equivalent: 748,157 vs 682,353 |
| 127 | + (+9.6%). No leak signal for either. |
| 128 | + |
| 129 | +Footprint context (not a leak): OE heap per row is ~2.3–5x vanillajs at 1k |
| 130 | +rows due to Region entry bookkeeping (baseline diagnostic finding 2). At the |
| 131 | +10k-row extension probe OE uses 9,511,356 bytes — below solid 10,570,464, |
| 132 | +svelte 11,603,029, vue 19,942,554 and preact-signals 38,801,488; above |
| 133 | +vanillajs 1,887,031 and lit 7,171,542. The micro churn `heapGrowthBytes` |
| 134 | +17,070,360 is a fake-DOM high-water reading without forced GC; the |
| 135 | +forced-GC browser probes above are the retention evidence and show return to |
| 136 | +baseline. The per-row footprint vs vanilla is carried as a documented |
| 137 | +trade-off under the same B3.4 evidence gate as finding 2; the blocker class |
| 138 | +as stated — growth/leaks — is CLEAR. |
| 139 | + |
| 140 | +### 4. Claim providing no meaningful work reduction — ACCEPTED TRADE-OFF |
| 141 | + |
| 142 | +Fake-DOM 1k-row table (`micro-evidence.json`): claim 10.387 ms vs fresh |
| 143 | +4.565 ms → claim/fresh ratio 2.275. On raw fake-DOM CPU time, claim is |
| 144 | +slower than bare fresh creation at this scale; that is documented in the |
| 145 | +baseline (finding 3) and not re-litigated here. |
| 146 | + |
| 147 | +The work-reduction evidence on the axes claim exists for: |
| 148 | + |
| 149 | +- Allocations: claim performs 0 allocations vs 10,031 for fresh creation of |
| 150 | + the same 1k-row table; 0 insertions/removals vs 10,031 insertions; 0 |
| 151 | + writes vs 8,034 attr writes. Node creation and GC pressure are eliminated, |
| 152 | + not reduced. |
| 153 | +- Versus the 0.43.3 baseline architecture (`benchmarks/v044/evidence.json`): |
| 154 | + claim allocations drop from 10–40 to 0 per scenario and initial walk |
| 155 | + visits from 10–40 to 0; claim timing is at or below fresh in 3 of 4 |
| 156 | + qualification scenarios (0.680/0.834, 0.243/0.287, 0.196/0.335 ms; |
| 157 | + nested-real-app 0.176 vs 0.147 ms). |
| 158 | +- Contract capability: the alternative to claim is not bare fresh creation |
| 159 | + but teardown of the server-rendered DOM plus fresh creation plus GC of its |
| 160 | + allocations, with loss of DOM identity and live state. Browser evidence: |
| 161 | + claim identity preserved and live value preserved on chromium, firefox and |
| 162 | + webkit, 0 page errors. Claim is the only path that satisfies the hydration |
| 163 | + contract (`docs/current/HYDRATION_CONTRACT.md`: the compiled claim |
| 164 | + artifact takes over the existing DOM in place). |
| 165 | + |
| 166 | +Disposition: claim delivers meaningful, measured work reduction on the |
| 167 | +allocation, node-creation and tree-walk axes (to exactly zero) and provides |
| 168 | +identity/state preservation that fresh creation cannot provide at any cost. |
| 169 | +The 2.275x fake-DOM CPU ratio at 1k-row scale is accepted as a documented |
| 170 | +trade-off; claim-path CPU optimization is deferred to the evidence-gated |
| 171 | +B3.4 work. This is not "claim ≈ full rerender": rerender pays teardown + |
| 172 | +fresh + GC and loses identity; claim pays none of those. |
| 173 | + |
| 174 | +### 5. Static route unexpectedly carrying mandatory runtime JS — CLEAR |
| 175 | + |
| 176 | +`benchmarks/v044/evidence.json` staticOutput: runtimeBytes 0, scriptTags 0, |
| 177 | +transferredBytes 219. The alpha.7 acceptance gate fails closed unless static |
| 178 | +runtime bytes stay at 0, and it passes at the review SHA. A static route |
| 179 | +carries no mandatory runtime JavaScript. |
| 180 | + |
| 181 | +## Overall admission verdict |
| 182 | + |
| 183 | +No BLOCKER. The Alpha.10 diagnostic baseline shows no structural |
| 184 | +contradiction with the architecture: the exact Part update path is |
| 185 | +explained and competitive (CLEAR), there is no memory/listener/subscription |
| 186 | +growth (CLEAR), static routes carry zero mandatory runtime JS (CLEAR), and |
| 187 | +the two known inefficiencies — O(distance) keyed Region moves and the |
| 188 | +fake-DOM claim/fresh CPU ratio at 1k-row scale, together with the per-row |
| 189 | +heap footprint vs vanilla — are bounded, documented, non-catastrophic |
| 190 | +ACCEPTED TRADE-OFFs deferred to the evidence-gated B3.4 performance work. |
| 191 | + |
| 192 | +This record satisfies the #1225 requirement for an explicit |
| 193 | +structural-regression verdict with evidence; per the issue's acceptance |
| 194 | +text, the verdict is to be recorded in the Beta.1 closure report by the |
| 195 | +owning role. This packet does not close #1225. |
| 196 | + |
| 197 | +## Commands and exit codes (this review, at 17ec6a57) |
| 198 | + |
| 199 | +- `git status` / `git log -1` → clean tree on `dev` at |
| 200 | + `17ec6a57610e9f4bdff090497ab34b973c884dfb` (exit 0) |
| 201 | +- `gh issue view 1225` / `gh issue view 1219` / `gh issue view 1150` → exit 0 |
| 202 | +- `git diff --stat 493548a6..17ec6a57 -- packages/element/src |
| 203 | + packages/adapter-vite/src packages/app/src packages/ui/src |
| 204 | + packages/create/src` → empty output (exit 0) |
| 205 | +- `deno test -A benchmarks/` → exit 0 (12 passed, 0 failed) |
| 206 | +- `jq` re-extraction of all cited medians, memory probes and micro op counts |
| 207 | + from `benchmarks/jfb/evidence.json`, `benchmarks/v044/micro-evidence.json` |
| 208 | + and `benchmarks/v044/evidence.json` → all values match |
| 209 | + `docs/current/v0.44.0-PERFORMANCE.md` (exit 0) |
0 commit comments