Skip to content

Add generated-Ok() code-size + execution-speed benchmark harness#270

Open
AaronWebster wants to merge 2 commits into
masterfrom
emboss/ok-bench-harness
Open

Add generated-Ok() code-size + execution-speed benchmark harness#270
AaronWebster wants to merge 2 commits into
masterfrom
emboss/ok-bench-harness

Conversation

@AaronWebster

Copy link
Copy Markdown
Collaborator

What

Adds a committed, CI-integrated harness for measuring the static code size and
execution speed of Emboss-generated C++ Ok() validation code across the
embedded targets the ongoing Ok()/switch codegen work cares about.

  • scripts/size_bench.py — compiles a fixed benchmark schema (testdata/benchmark.emb)
    plus the many_conditionals Ok() highlight across a target × compiler × opt-level
    matrix, reporting per-TU .text (size) and per-symbol Ok() size (nm -S, nested->
    demangle regex). --revisions BASE HEAD holds the schema fixed (pulled forward from
    head) so only the generator/runtime under test varies; --speed adds a dynamic run.
  • scripts/size_comment.py — renders the JSON into a sticky Markdown PR comment.
  • .github/workflows/code-size.yml — provisions the toolchains, baselines against the
    merge-base (not the base tip, so codegen that lands on the base after a PR branches
    isn't misattributed), and posts/updates one sticky size comment per PR.
  • scripts/emboss_insncount.c + scripts/build_qemu_plugin.sh — a plugin-enabled
    qemu-user build + TCG plugin for the deterministic retired-instruction speed metric.

Targets

  • ARM Cortex-M4 / Thumb-2 (arm-none-eabi-g++, size) + arm-linux-gnueabihf (speed).
  • MicroBlaze big-endian (Bootlin microblazebe).
  • MicroBlaze little-endian — the shipped config. The authoritative size target is
    AMD/Xilinx Vitis 2025.2 LE v11.0 (mb-g++), which is proprietary and
    locally-licensed, so have() skips it in public CI; the open-source Bootlin
    microblazeel build is the CI LE proxy and provides the LE speed binary.
  • x86-64 (g++/clang++) as a reference.

Speed metric

Deterministic guest retired-instruction count under a plugin-enabled qemu-user (both
MicroBlaze endiannesses + ARM), plus host wall-clock on x86. The distro qemu-user has no
plugin support and qemu 10 dropped contrib/plugins/libinsn.c, so the harness builds
qemu 10.0.11 --enable-plugins and ships its own instruction-count plugin; when absent,
the speed run degrades to wall-clock and the size report is unaffected.

Checkpoint 0 baseline (verified against master)

LargeConditionals::Ok() reader symbol @ -Os:

Target Ok() bytes
x86-64 (g++) 3048
ARM Cortex-M4 4628
MicroBlaze BE 7704
MicroBlaze LE (Vitis, authoritative) 8156

Speed @ -Os, retired instructions: ARM 9.32B · MB-BE 12.75B · MB-LE 12.17B; x86 host
wall-clock 0.42 s.

Supersedes

Replaces the earlier code-size tooling PRs, folding their coverage into one CI-integrated
harness:

New here beyond all three: MicroBlaze LE (Vitis authoritative + Bootlin proxy), the
dynamic execution-speed metric, and four per-instantiation Ok() driver TUs
(reader-only / both / writer-only / residual-heavy) consumed by the follow-on
optimization PRs.

Adopt the size-benchmark tooling from the #265->#267 branch stack
(scripts/size_bench.py, scripts/size_comment.py,
.github/workflows/code-size.yml, testdata/benchmark.emb) and extend it into
the committed size+speed harness the Ok() optimization work needs, superseding
the #252 embedded-bench scripts while folding their coverage back in.

Size measurement (size_bench.py):
- Add MicroBlaze little-endian targets: the AMD/Xilinx Vitis v11 toolchain
  (the actually-shipped target, authoritative for LE size; local-only, since it
  is proprietary), plus an open-source Bootlin microblazeel proxy for CI where
  Vitis is absent. Rename the existing MicroBlaze target to MicroBlaze BE.
  Per-compiler env support threads Vitis' LD_LIBRARY_PATH through every
  compiler/binutils call.
- Fold back #252's dual-Ok coverage: measure DisjunctionConditionals::Ok()
  alongside LargeConditionals::Ok().
- Add four Ok() driver TUs the later optimization phases consume: reader-only,
  both-instantiated, writer-only, and residual-heavy (stubbed on
  DisjunctionConditionals until an OuterGuardedUnion schema lands). Reader vs
  writer Ok() symbols are split on the `const` in the demangled storage arg.

Execution speed (opt-in --speed):
- Deterministic retired-instruction count under qemu-user + a bundled TCG
  plugin (scripts/emboss_insncount.c; QEMU 10.x dropped contrib libinsn) for
  ARM/MicroBlaze BE/LE, plus native host wall-clock. The plugin-enabled qemu is
  built from source by scripts/build_qemu_plugin.sh and located via
  EMBOSS_QEMU_DIR/EMBOSS_QEMU_PLUGIN; when absent, speed degrades to host
  wall-clock and the size report is unaffected.

CI (code-size.yml): provision the Bootlin microblazeel LE toolchain and,
best-effort and cached, the plugin-enabled qemu so the per-PR comment can carry
LE size and (when the build succeeds) retired-instruction speed. size_comment.py
gains a Disj Ok() column and a speed table.

Checkpoint 0: on origin/master this reproduces the verified LargeConditionals::
Ok() baseline exactly -- x86 3048, ARM 4628, MicroBlaze BE 7704, Vitis LE 8156
-- and emits a retired-instruction speed number per target.
Two measurement-only corrections to the Ok() size/speed harness that were
derived while running later phases and are owed back to this branch:

- Repoint the `residual_heavy` size driver and the `--speed` hot loop from
  the placeholder `DisjunctionConditionals` to `OuterGuardedUnion`, the
  residual-guarded schema that the residual-read-elimination work adds to
  testdata/many_conditionals.emb. Until that schema lands the driver simply
  fails to compile and is skipped (compile_obj returns False), so the size
  report is unaffected; once it lands, residual_heavy measures Lever A.

- Build every Ok()/benchmark driver view from `unsigned char*` /
  `const unsigned char*` instead of `char*`. `char*` deduces
  ContiguousBuffer<char, ...>, which is not the canonical
  ReadWriteContiguousBuffer/ReadOnlyContiguousBuffer (unsigned char) that
  Emboss's own View/Writer aliases and real uint8_t-buffer users hit. Any
  codegen conditioned on the exact storage type is otherwise invisible to
  the harness.
@github-actions

Copy link
Copy Markdown

📐 Generated code size & instructions

b9f89e357 → 8894093af · smaller is better · ✅ no change

Code size (compiled .text bytes) and Instructions (objdump count) are totals for the generated code of the all-features benchmark.emb fixture. Large Ok() / Disj Ok() are the sizes (bytes) of the optimized LargeConditionals and DisjunctionConditionals validation methods, highlights. Δ vs the merge-base · 🟢 smaller / 🔴 larger.

-Os (embedded)

Target · Compiler Code size Instructions Large Ok() Disj Ok()
x86-64 · gcc 12842 B 2364 2384 B 407 B
x86-64 · clang 6258 B 873 1460 B 290 B
ARM Cortex-M4 · gcc 7146 B 1799 1948 B 212 B
MicroBlaze BE · gcc 10957 B 2452 7704 B 540 B
MicroBlaze LE (Bootlin) · gcc 11229 B 2520 7704 B 540 B
-O2 / -O0

-O2

Target · Compiler Code size Instructions Large Ok() Disj Ok()
x86-64 · gcc 5125 B 868 9048 B 343 B
x86-64 · clang 3086 B 451 4608 B 189 B
ARM Cortex-M4 · gcc 3322 B 851 4768 B 180 B
MicroBlaze BE · gcc 6449 B 1325 10200 B
MicroBlaze LE (Bootlin) · gcc 5461 B 1078 15608 B 548 B

-O0

Target · Compiler Code size Instructions Large Ok() Disj Ok()
x86-64 · gcc 100054 B 19618 6442 B 900 B
x86-64 · clang 76530 B 13895 7278 B 785 B
ARM Cortex-M4 · gcc 59564 B 19289 6788 B 712 B
MicroBlaze BE · gcc 107420 B 23868 8276 B 1280 B
MicroBlaze LE (Bootlin) · gcc 105340 B 23348 8276 B 1280 B

Compilers: x86-64 gcc 13.3.0 · x86-64 clang 18.1.3 · ARM Cortex-M4 gcc 13.2.1 · MicroBlaze BE gcc 14.3.0 · MicroBlaze LE (Bootlin) gcc 14.3.0. benchmark.emb is a fixed fixture (Ok()+CopyFrom over every top-level view); it is pulled forward from head, so only the code generator under test varies between base and head.

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