Skip to content

perf(target): enable unaligned-scalar-mem for riscv64im guests - #864

Merged
MauroToscano merged 2 commits into
mainfrom
fix/target-unaligned-scalar-mem
Jul 27, 2026
Merged

MauroToscano merged 2 commits into
mainfrom
fix/target-unaligned-scalar-mem

Conversation

@diegokingston

Copy link
Copy Markdown
Collaborator

Summary

Adds +unaligned-scalar-mem to the riscv64im-lambda-vm-elf target spec. This lets LLVM lower potentially-unaligned u64 accesses (align-1 loads/stores, e.g. u64::from_le_bytes on byte buffers in the keccak absorb path, ptr::read_unaligned) to single ld/sd instructions instead of byte-assembled lbu/slli/or and sb/srli chains.

Sound on this target: the VM tolerates unaligned doubleword accesses — executor/src/vm/memory.rs load_doubleword/store_doubleword byte-assemble them host-side at the same 1-instruction cost as aligned ones.

Measured guest cycles (cli execute --cycles, deterministic)

Fixture Baseline Patched Delta
ethrex_empty_block 994,639 763,430 −23.3%
ethrex_simple_tx 1,791,453 1,445,399 −19.3%
ethrex_10_transfers 6,810,436 6,200,610 −9.0%

The % shrinks with tx count as keccak/hashing becomes a smaller share of total cycles.

Verification

  • Codegen: keccak provider absorb loop goes from 8×lbu+slli/or per lane to one ld per lane (disassembly of the built ethrex guest, before/after in commit body).
  • Minimal no_std PoC (*lane ^= u64::from_le_bytes(buf), read_unaligned, write_unaligned): baseline emits byte chains, patched emits single ld/sd.
  • cargo test -p executor --test rust test_keccak passes with a patched guest ELF; keccak guest cycle count unchanged (aligned workload).
  • CI note: ELF artifact cache keys in .github/workflows/pr_main.yaml hash the target JSON, so cached guest ELFs invalidate naturally.

Add +unaligned-scalar-mem to the riscv64im-lambda-vm-elf target spec so
LLVM lowers potentially-unaligned u64 accesses (align-1 loads/stores, e.g.
u64::from_le_bytes on byte buffers, ptr::read_unaligned) to single ld/sd
instructions instead of byte-assembled lbu/slli/or and sb/srli chains.

Sound on this target: the VM tolerates unaligned doubleword accesses --
executor/src/vm/memory.rs load_doubleword/store_doubleword byte-assemble
them host-side at the same 1-instruction cost as aligned accesses.

Verified:
- Minimal no_std PoC (absorb-like *lane ^= u64::from_le_bytes(buf),
  read_unaligned, write_unaligned): baseline emits 8x lbu + slli/or
  chains (load) and 8x sb + srli chains (store); with the feature both
  lower to a single ld / sd.
- executor/programs/rust/keccak guest rebuilt with the patched spec:
  lbu 70 -> 50, sb 112 -> 104, slli/or reduced, widened to lh/lhu/lw.
- cargo test -p executor --test rust test_keccak passes with the
  patched ELF (VM commits keccak256("hello world!") matching host
  tiny-keccak); CLI execute succeeds with identical cycle count (7468)
  vs baseline.

CI note: the ELF artifact cache keys in .github/workflows/pr_main.yaml
hash executor/programs/riscv64im-lambda-vm-elf.json, so cached guest
ELFs invalidate naturally on this change.
@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench

@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench

@github-actions

github-actions Bot commented Jul 25, 2026 •

Copy link
Copy Markdown

Benchmark — ethrex 20 transfers (median of 3)

Table parallelism: auto (cores / 3)

Metric main PR Δ
Peak heap 57086 MB 51336 MB -5750 MB (-10.1%) 🟢
Prove time 30.840s 27.184s -3.656s (-11.9%) 🟢

🎉 Improvement detected — heap or time decreased by more than 5%.

✅ Low variance (time: 1.6%, heap: 3.9%)

Commit: c4e42b2 · Baseline: cached · Runner: self-hosted bench

@MauroToscano
MauroToscano added this pull request to the merge queue Jul 27, 2026
Merged via the queue into main with commit 0893b6b Jul 27, 2026
15 checks passed
@MauroToscano
MauroToscano deleted the fix/target-unaligned-scalar-mem branch July 27, 2026 14:11
jotabulacios added a commit that referenced this pull request Sep 9, 2026
compiler_builtins enables its mem-unaligned feature for x86, aarch64 and bpf
only, so on RISC-V every misaligned word inside memcpy and memmove is
reassembled with a load, two shifts, an or and a store. This VM does not need
that: #864 already builds guests with +unaligned-scalar-mem because the
executor byte-assembles an unaligned doubleword host-side at the same
one-instruction cost as an aligned one, so the rest of the guest has been
emitting unaligned loads since July and only these two functions avoided them.

memcpy is the largest leaf in an ethrex block profile at 25.15% of guest
cycles, and its biggest caller is rkyv deserializing the witness trie.

On the real mainnet block 25368371 this is -8.33% guest cycles, 8 epochs down
to 7, -6.33% proving time, -8.57% proof size, -9.47% verify and -4.78%
base-field-equivalent cells; keccak and ecsm call counts are unchanged.
tooling/ethrex-tests passes 6/6 including the two --ignored cases, and both
arms of every A/B verify OK. Unaligned accesses land on the more expensive
MEMW table rather than MEMW_A, so prover area was measured rather than
assumed.

The key is scoped to the guest target, so host builds read the file and take
nothing from it. Cargo joins rustflags arrays across ancestor config files, so
each guest crate keeps its own flags and gains this one. The feature cannot be
reached through -Z build-std-features: that chain only forwards
compiler-builtins-mem and no compiler-builtins-mem-unaligned exists.
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