Skip to content

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

Open
MauroToscano wants to merge 2 commits into
mainfrom
perf/ethrex-guest-thin-lto
Open

perf(programs): build the ethrex guest with thin LTO#861
MauroToscano wants to merge 2 commits 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

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown

Benchmark — ethrex 20 transfers (median of 3)

Table parallelism: auto (cores / 3)

Metric main PR Δ
Peak heap 57086 MB 55038 MB -2048 MB (-3.6%) ⚪
Prove time 30.840s 29.826s -1.014s (-3.3%) ⚪

✅ No significant change.

✅ Low variance (time: 3.0%, heap: 1.3%)

Commit: 676f28d · Baseline: cached · Runner: self-hosted bench

@diegokingston

Copy link
Copy Markdown
Collaborator

/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.

2 participants