Skip to content

Actualize benchmarks README and fix chart-delta layout#19

Merged
intech merged 4 commits intomainfrom
chore/actualize-benchmarks-readme-and-chart
Apr 21, 2026
Merged

Actualize benchmarks README and fix chart-delta layout#19
intech merged 4 commits intomainfrom
chore/actualize-benchmarks-readme-and-chart

Conversation

@intech
Copy link
Copy Markdown

@intech intech commented Apr 20, 2026

Summary

benchmarks/README.md

The "Current results" section carried pre-L0 hand-curated numbers that contradicted the auto-generated table a few paragraphs above. Replaced with a "Reading the results" section that explains the L0+L1+L2 stack and the measured ~0.80x-protobufjs floor. "Future work" loses the "CI integration" item (shipped in #9 / #15) and gains realistic next steps (ts-proto comparison, multi-shape in CI matrix, decoder fast path).

chart-delta.svg layout fix

Addresses reader complaints about overlapping text and tight top margin:

  • Top margin 80 -> 120 px — legend + axis tick labels no longer collide with the subtitle
  • Legend shifted 100 px left of the chart body (matches the fixture-label column visually)
  • Axis tick labels nudged up 5 px clear of grid edge
  • Per-row half-bar positions shifted 3 px further from the split — no more overlap of numeric labels between stacked vs-toBinary / vs-protobufjs bars

Chart regenerated via `npm run bench:report` on main; committed alongside the helper change so the committed SVG matches the updated generator.

intech and others added 4 commits April 20, 2026 23:16
Per design spec (analysis/p1-t6-l3-design-spec.md). Observes message
shapes across first 10 encodes per schema; graduates frequent shapes
to specialized plan variants that skip the generic `isFieldSet`
presence gate for known-present fields and known-absent slots. 4-
variant cap with seal-on-breach prevents cache explosion.

Two execution modes:
- Mode A (CSP-safe, default): variant = pre-computed `VariantStep[]`
  list of known-present slots; executor is a statically-imported
  interpreter that delegates to the L1+L2 `estimate*/write*`
  helpers. Safe under strict CSP.
- Mode B (CSP-unsafe, opt-in): per-variant `new Function()` executor
  with unrolled call sites for per-variant IC isolation. Enabled
  via `globalThis[Symbol.for('@bufbuild/protobuf.adaptive-codegen')] = true`.

Spec adaptations for current main:
- The L1+L2 reference implementation on main is the direct estimate/
  write function set in `to-binary-fast.ts` rather than the opcode-
  based `schema-plan.ts` assumed by the spec. The variant plan shape
  therefore drops the opcode trim/filter step and instead carries a
  compact `VariantStep[]` — same monomorphization effect, fewer
  moving parts for this code base.
- `buildVariantExecutor` is replaced by the two closures (Mode A
  static / Mode B codegen) in `compileVariantPlan` with identical
  semantics.

Gates (5-run median on pinned CPU):
- Byte-parity: preserved across 11 new L3 tests + 16 pre-existing
  toBinaryFast tests + correctness-matrix.
- SimpleMessage multi-shape:   +55.5%  (spec target: >= +10%)
- SimpleMessage single-shape:  +40.8%  (spec target: regression <= 3%)
- Span multi-shape:            +19.0%  (spec target: >= +10%)
- Span single-shape:           +12.2%  (spec target: regression <= 3%)
- Memory overhead: bounded by D3 + D7 (shared side tables).

Opt-in: `toBinaryFast(schema, msg, { adaptive: true })` or
`PROTOBUF_ES_L3=1`. Default behaviour unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…chains

- schema-plan-adaptive.ts: remove suppressions for a rule biome isn't
  enforcing in this project config
- schema-plan-adaptive.ts + to-binary-fast.ts: collapse defensive
  globalThis process env lookups into optional chain form
- biome.json: ignore gen/gen-protobufjs/.tmp from root scope so turbo
  lint doesn't catch pbjs-generated files and scratch dirs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README:
- Drop the pre-L0 "Current results" section (stale hand-curated numbers
  that contradicted the auto-generated table a few paragraphs above)
- Add a "Reading the results" section that explains the L0+L1+L2 stack
  and the ~0.80x-protobufjs floor in plain language
- Replace "Future work: CI integration" (already shipped) with the
  next-real-work items (ts-proto comparison, multi-shape in CI matrix,
  fromBinaryFast)

chart-delta.svg (generator in report-helpers.ts):
- Top margin 80 -> 120 px so axis tick labels do not collide with
  legend, and legend does not collide with subtitle
- Legend now anchored 100 px left of the chart body (was flush with
  chart-left) — matches the fixture-label column visually
- Axis tick labels pushed 5 px higher so they clear the top of the
  chart grid
- Per-row bar positions shifted 3 px further from the center split so
  numeric labels on the two half-bars never overlap each other

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@intech intech self-assigned this Apr 20, 2026
@github-actions
Copy link
Copy Markdown

Benchmark: no regressions

Thresholds: throughput regression >5%, memory regression >10%. Runner pinned to CPU 0 via taskset. Current run on linux/x64, Node v22.22.2, captured 2026-04-20T20:07:11.719Z.
Baseline captured 2026-04-20T17:34:45.007Z on linux/x64, Node v22.22.2.

Summary: 0 regressed, 0 improved, 0 new, 20 unchanged.

Fixture Baseline ops/s PR ops/s Δ ops Baseline B/op PR B/op Δ mem Status
SimpleMessage :: toBinary (pre-built, 19 B) 849,817 864,589 +1.7% ok
ExportTraceRequest (100 spans) :: toBinary (pre-built, 32926 B) 1,231 1,223 -0.7% ok
ExportMetricsRequest (50 series) :: toBinary (pre-built, 17696 B) 2,168 2,150 -0.8% ok
ExportLogsRequest (100 records) :: toBinary (pre-built, 21319 B) 2,171 2,185 +0.7% ok
K8sPodList (20 pods) :: toBinary (pre-built, 28900 B) 2,342 2,334 -0.4% ok
GraphQLRequest :: toBinary (pre-built, 624 B) 176,305 176,628 +0.2% ok
GraphQLResponse :: toBinary (pre-built, 1366 B) 236,876 238,673 +0.8% ok
RpcRequest :: toBinary (pre-built, 501 B) 296,046 298,448 +0.8% ok
RpcResponse :: toBinary (pre-built, 602 B) 434,888 431,352 -0.8% ok
StressMessage (depth=8, width=200) :: toBinary (pre-built, 12868 B) 7,860 7,916 +0.7% ok
SimpleMessage :: fromBinary (19 B) 1,020,891 1,021,021 +0.0% ok
ExportTraceRequest (100 spans) :: fromBinary (32926 B) 599.8 611.0 +1.9% ok
ExportMetricsRequest (50 series) :: fromBinary (17696 B) 1,149 1,159 +0.9% ok
ExportLogsRequest (100 records) :: fromBinary (21319 B) 1,073 1,078 +0.5% ok
K8sPodList (20 pods) :: fromBinary (28900 B) 1,398 1,394 -0.3% ok
GraphQLRequest :: fromBinary (624 B) 300,513 305,028 +1.5% ok
GraphQLResponse :: fromBinary (1366 B) 265,540 271,693 +2.3% ok
RpcRequest :: fromBinary (501 B) 269,405 274,366 +1.8% ok
RpcResponse :: fromBinary (602 B) 378,014 378,938 +0.2% ok
StressMessage (depth=8, width=200) :: fromBinary (12868 B) 4,046 3,997 -1.2% ok

Produced by benchmarks/scripts/compare-results.ts. Artifacts: bench-results-<pr> (current), bench-baseline-main (baseline).

@intech intech merged commit fab8886 into main Apr 21, 2026
28 of 29 checks passed
@intech intech deleted the chore/actualize-benchmarks-readme-and-chart branch April 21, 2026 11:10
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