Skip to content

perf(programs): build the ethrex guest with thin LTO#861

Open
MauroToscano wants to merge 1 commit into
mainfrom
perf/ethrex-guest-thin-lto
Open

perf(programs): build the ethrex guest with thin LTO#861
MauroToscano wants to merge 1 commit into
mainfrom
perf/ethrex-guest-thin-lto

Conversation

@MauroToscano

Copy link
Copy Markdown
Contributor

What

Add a [profile.release] with lto = "thin" to the ethrex guest crate
(executor/programs/rust/ethrex/Cargo.toml). The crate previously had no
[profile.release] at all, so it built with cargo's release defaults
(lto = false, codegen-units = 16).

Why

Thin LTO measurably lowers the number of instructions the VM executes for a
real ethrex block, at no runtime cost — execution in this VM is priced per
executed instruction, so the small ELF-size increase is free.

Measured on the committed fixtures (same toolchain, same inputs):

fixture baseline (cycles) thin-LTO (cycles) Δ cycles Δ % Keccak Ecsm
ethrex_bench_4.bin 3,841,262 3,750,291 −90,971 −2.37% 174 16
ethrex_bench_16.bin 11,616,950 11,352,127 −264,823 −2.28% 356 64

The syscall invariants are identical before and after (Keccak 174 / Ecsm 16 for
_4, Keccak 356 / Ecsm 64 for _16) — the guest does exactly the same work;
LTO just compiles it into fewer executed instructions.

Methodology

Deterministic A/B on a dedicated 32-core rig, same toolchain
(nightly-2026-02-01, -Z build-std, riscv64im-lambda-vm-elf), cycles read
via cli execute --cycles on the committed fixtures. The measurement noise
floor is exactly 0: the pristine baseline was built twice in separate
CARGO_TARGET_DIRs and produced byte-identical ELFs, so the deltas above are
the whole signal.

Size

Guest ELF grows 3.64 MB → 3.82 MB (+4.9%). This is free here: the VM charges
per executed instruction, not by ELF size.

Follow-up (out of scope)

A tuned variant — thin LTO plus -C llvm-args=-inline-threshold=2500 /
-unroll-threshold=2000 — measured a further ~2× improvement
(ethrex_bench_16 11,035,843, −5.00% total; ethrex_bench_4 3,616,998,
−5.84% total) at +66% ELF size. That inline/unroll tuning is deliberately left
out of this PR and can land as a separate change.

The ethrex guest crate had no [profile.release], so it built with cargo's
release defaults (lto = false, codegen-units = 16). Adding thin LTO lowers the
number of instructions the VM executes for a real ethrex block, at no runtime
cost — execution is priced per executed instruction, so the small ELF-size
increase (3.64 MB -> 3.82 MB) is free.

Measured on the committed fixtures (same toolchain, same inputs):

  ethrex_bench_4.bin:   3,841,262 -> 3,750,291 cycles (-90,971, -2.37%)
  ethrex_bench_16.bin: 11,616,950 -> 11,352,127 cycles (-264,823, -2.28%)

Syscall invariants are identical before and after (Keccak 174 / Ecsm 16 for _4,
Keccak 356 / Ecsm 64 for _16): the guest does exactly the same work, LTO just
compiles it into fewer executed instructions.
@MauroToscano

Copy link
Copy Markdown
Contributor Author

/bench

@github-actions

Copy link
Copy Markdown

Benchmark — ethrex 20 transfers (median of 3)

Table parallelism: auto (cores / 3)

Metric main PR Δ
Peak heap 73028 MB 71902 MB -1126 MB (-1.5%) ⚪
Prove time 37.771s 37.731s -0.040s (-0.1%) ⚪

✅ No significant change.

🔬 Looks like a small speedup (-0.1%) — below what 3 runs can confirm. Comment /bench-abba to run the drift-free ABBA tiebreaker (paired-t CI + exact Wilcoxon). Note: it occupies the bench server for ~30–40 min.
Optional pair count: /bench-abba 32 (20 resolves ~1%, 32 for ~0.6%).

✅ Low variance (time: 0.8%, heap: 1.8%)

Commit: 9461f88 · Baseline: cached · Runner: self-hosted bench

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