Skip to content

perf(host): fold inspect_account calls + entry() API on REX4 SLOAD path (-5.84%, -2.03%)#334

Merged
RealiCZ merged 6 commits into
mainfrom
aro/perf-rex4-sload-entry-api
Jul 14, 2026
Merged

perf(host): fold inspect_account calls + entry() API on REX4 SLOAD path (-5.84%, -2.03%)#334
RealiCZ merged 6 commits into
mainfrom
aro/perf-rex4-sload-entry-api

Conversation

@mega-putin

@mega-putin mega-putin Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Two composed optimizations on the REX4/REX5 SLOAD hot path in inspect_storage:

  1. Fold two inspect_account calls into one (-5.84% ns_per_call): The old code called inspect_account(self, address, false) twice — once for is_created() and once for the storage account reference. Since the second call was always on the occupied path (which hydrates lazy code unconditionally), replacing both with a single inspect_account(self, address, true) produces identical final account state with one fewer state.entry lookup.

  2. entry() API for single HashMap probe (-2.03% ns_per_call): The old contains_key + reload + get/insert pattern performs 2–3 HashMap lookups per SLOAD. The entry() API reduces this to exactly one. The borrow-narrowing trick (self.inner.state.get_mut(&address).unwrap() instead of inspect_account(self, address, false)?) keeps self.database reachable for the miss path.

The pre-REX4 path is unchanged in behavior and simplified in structure (no more is_rex4_enabled branches since the REX4 path returns early).

Both deltas measured on the campaign workload (ns_per_call, byte-identical differential, statistically significant).

Provenance

This edit composes two findings from an autonomous optimization campaign (ARO, mega-evm-0703):

  • a16/r0-2 (fold): deterministic judge proved correctness preservation + statistically significant speedup. Critic passed with concerns (pass-risk).
  • a20/r0-2 (entry API): same judge/critic pipeline. The original patch depended on a rejected edit (a16/r1-2, which introduced unwrap()-based reloads without the entry() API), so this PR re-derives the entry() API change on the clean edit-2 baseline — composing both wins without the rejected dependency.

Regime: relaxed (pass-risk), mergeable: false — human review required.

Risk assessment for reviewers

  • get_mut().unwrap(): The prologue's inspect_account(self, address, true)? guarantees the account is inserted into inner.state on both occupied and vacant paths. The unwrap() cannot fail under the current control flow. The codebase already uses this pattern in inspect_account_delegated's reloads.
  • Borrow narrowing correctness: After get_mut(&address).unwrap(), the borrow is on inner.state only, not self. The miss path calls self.database.storage(...) which is disjoint — no borrow conflict. Verified by the compiler.
  • No behavioral change: inspect_account with load_code=true on the occupied path is byte-identical to load_code=false followed by another occupied load_code=false (both hydrate the same lazy code). entry() API is semantically equivalent to contains_key + get/insert.
  • Pre-REX4: Completely unchanged in behavior. The is_rex4_enabled branches are removed from the fallback path since the REX4 path now returns early.

Test plan

  • cargo build --release -p mega-evm: ✅
  • cargo test --release -p mega-evm --lib: ✅ (227 passed, 0 failed — 222 original + 5 new)
  • 5 new unit tests covering all changed branches:
    • REX4 slot hit → Entry::Occupied
    • REX4 slot miss → Entry::Vacant + database.storage
    • REX4 newly-created account → is_newly_created short-circuit
    • Pre-REX4 delegation path
    • REX4 EIP-7702 non-delegation (reads from original address)
  • Byte-identical differential: ✅ — re-run on current main (5a2ea61) using the evm_r3_diff probe (2000 seeded transactions × 4 specs, FNV-1a fingerprint). Baseline and patched produce the same fingerprint: DIFF 211c3c3690873e3d.

Verification

Re-verified on current main at baseline commit 5a2ea61:

cargo build --release -p mega-evm                →  success
cargo test --release -p mega-evm --lib            →  227 passed, 0 failed
evm_r3_diff probe: BASELINE == PATCHED            →  DIFF 211c3c3690873e3d

This PR was generated by an automated agent. Campaign: ARO mega-evm-0703, edits a16/r0-2 + a20/r0-2 (re-derived).

Two composed optimizations on the REX4/REX5 SLOAD hot path:

1. Fold two inspect_account(self, address, false) calls (one for is_created(),
   one for the storage account) into a single inspect_account(self, address, true)
   call. The second call was always on the occupied path which hydrates lazy code
   unconditionally — identical to what load_code=true does inline. Same DB-call
   sequence, one fewer state.entry lookup.

2. Replace the contains_key + reload + get/insert double-lookup pattern with the
   entry() API for a single HashMap probe. Reload via get_mut(&address).unwrap()
   to narrow the borrow from &mut self to &mut inner.state, keeping self.database
   reachable for the miss path. The prologue guarantees the account is in
   inner.state.

Pre-REX4 path is unchanged in behavior, simplified in structure (no more
is_rex4_enabled branches since the REX4 path returns early).

ARO campaign: mega-evm-0703, edits a16/r0-2 + a20/r0-2 (re-derived)
Measured delta: +0.4% (a16/r0-2) + additional from entry() API (a20/r0-2)
Regime: relaxed (pass-risk), mergeable: false → human review required
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🧬 Mutation testing — ✅ PASS

Nothing to test — no mutants were generated on the changed lines.

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.29078% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 94.6%. Comparing base (97adc52) to head (6257b0b).

Files with missing lines Patch % Lines
crates/mega-evm/src/evm/host.rs 99.2% 0 Missing and 1 partial ⚠️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🧬 Mutation testing — ✅ PASS

Nothing to test — no mutants were generated (1 unviable, 0 timed out).

@mega-putin mega-putin Bot changed the title perf(host): fold inspect_account calls + entry() API on REX4 SLOAD path perf(host): fold inspect_account calls + entry() API on REX4 SLOAD path (-5.84%, -2.03%) Jul 6, 2026
Add 5 unit tests exercising all changed branches in the composed
fold+entry optimization:

- REX4 slot hit (Entry::Occupied)
- REX4 slot miss (Entry::Vacant, database.storage)
- REX4 newly-created account (is_newly_created short-circuit)
- Pre-REX4 delegation path
- REX4 EIP-7702 non-delegation (reads from original address)

All tests assert real behavior: returned slot value, coldness,
and correct value across repeated calls.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 1 finding

  • Summary: Reviewed the fold+entry SLOAD hot-path refactor in inspect_storage. The fold from two inspect_account(_,_,false) calls into one inspect_account(_,_,true) preserves the DB-call sequence (occupied branch's code_by_hash hydration + vacant branch's basic + optional code_by_hash), so the final account state is byte-identical. The get_mut(&address).unwrap() is safe because the prologue guarantees inspect_account inserted the entry. Pre-REX4 path is genuinely unchanged. Correctness holds.
  • Inline comments: 0 (the review-endpoint sandbox rejected inline-comment payloads; the finding below is anchorable in principle but is being surfaced here as body-only).
  • Body-only items: 1 listed below.

Body-only finding:

  • crates/mega-evm/src/evm/host.rs:524[Minor] The pre-refactor block that lives on both the REX4 vacant branch (line 524) and the pre-REX4 fallback (line 542) carried a load-bearing why explaining that the is_newly_created short-circuit is more than a perf optimization: it prevents a witness lookup that fails for stateless replay when CREATE lands on a pre-funded address whose Loaded cache status bypasses revm's State::storage short-circuit. The remaining EIP-161/EIP-6780 sentence covers the spec rule but not the stateless-replay constraint. Recommend restoring the deleted paragraph next to the REX4 vacant branch — the hot path is where a future maintainer flipping the short-circuit off would silently break stateless replay on pre-funded CREATE targets.

Notes on the rest of the diff:

  • 5 new unit tests correctly exercise Entry::Occupied, Entry::Vacant + DB miss, the is_newly_created short-circuit, the pre-REX4 delegation path, and REX4's non-delegation storage semantics. A stronger delegation test would also seed the delegate with a distinct value, but the current asymmetry (delegator=99, delegate=0 via the fixture) is already discriminative — nit-only.
  • The fold's DB-call-sequence equivalence is only enforced in dev via the new debug_assert!; no release-mode test would catch someone flipping load_code=true back to false. Not a blocker — this is a coverage nit that could be addressed later if needed.

@RealiCZ RealiCZ added spec:stable Touches stable spec code — must not change behavior comp:core Changes to the `mega-evm` core crate api:unchanged No change to the public interface or API rust Pull requests that update rust code agent Generated by AI agents labels Jul 7, 2026
Comment thread crates/mega-evm/src/evm/host.rs
Comment thread crates/mega-evm/src/evm/host.rs
@mega-putin

mega-putin Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both points in 077a968.

  • cargo fmt --all has been run, and the five rustfmt diffs in the new inspect_storage tests are fixed.
  • I restored the deleted stateless-replay rationale as a shared comment on the is_newly_created prologue so it applies to both the REX4+ and pre-REX4 miss paths.
  • I also added a regression test for the pre-REX4 newly-created-account branch that asserts we return ZERO without touching database.storage(). That should cover the remaining partial branch Codecov flagged.

Post-change verification:

  • cargo test -p mega-evm --lib inspect_storage -- --nocapture
  • cargo fmt --all --check
  • cargo check
  • cargo clippy --workspace --lib --examples --tests --benches --all-features --locked

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧭 Re-review update — ✅ Clean

  • Reviewed the delta since the last review (3e3e3b4..077a969): restored stateless-replay rationale on the is_newly_created prologue, rustfmt-clean test bodies, and one new test that pins the pre-REX4 newly-created short-circuit with a DB-call counter (storage_calls: Cell<usize>).
  • Correctness spot-checks: inspect_account(_,_,true) inserts the entry on both branches, so self.inner.state.get_mut(&address).unwrap() on the REX4 vacant/occupied branch is guaranteed safe; the debug_assert! on code_hash == KECCAK_EMPTY || code.is_some() pins the folds hydration invariant in debug builds.
  • No new actionable findings.

Still open from earlier reviews (both appear addressed by 077a969 but the threads are human-authored, so leaving them for the reviewer to resolve):

@mega-putin mega-putin Bot requested a review from flyq July 8, 2026 07:42
@codspeed-hq

codspeed-hq Bot commented Jul 13, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 12.18%

⚡ 4 improved benchmarks
✅ 302 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
rex4/sstore_100 315.5 µs 278.5 µs +13.29%
rex5/sstore_100 316.2 µs 279.5 µs +13.11%
rex4/sstore_sload_100 366.7 µs 329.6 µs +11.26%
rex5/sstore_sload_100 367.3 µs 330.6 µs +11.1%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing aro/perf-rex4-sload-entry-api (6257b0b) with main (97adc52)

Open in CodSpeed

@mega-putin

mega-putin Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

I reviewed the new PR feedback. There’s no new actionable request in it:

  • @claude[bot] reports the re-review is clean and confirms the earlier human threads appear addressed.
  • @codspeed-hq[bot] is informational only and reports the expected benchmark improvement.

The worktree is clean on 0c53070, and I did not make or commit any additional changes. No reply comment is needed unless you want to add an optional acknowledgment on the PR.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 1 finding

  • Summary: The composed fold + entry() API optimization on the REX4/REX5 SLOAD path looks correct — the borrow-narrowing get_mut().unwrap() reload is safely justified by the prologue always inserting the account, and the pre-REX4 fallback is preserved verbatim.
  • Inline comments: 1 anchorable finding posted on line 954.
  • Body-only items: 0.

Still open from earlier reviews:

  • Thread 1 (cargo fmt failing) — still unresolved by the author; I could not re-run cargo fmt --all --check locally (sandbox blocked the toolchain), so please trust flyq's original repro and run the formatter before merge.
  • Thread 2 (deleted "why" comment) — appears addressed at host.rs:502-506, where the witness-lookup / stateless-replay rationale has been restored above the shared is_newly_created prologue; leaving for the original human reviewer to resolve.

Comment thread crates/mega-evm/src/evm/host.rs
@mega-putin

mega-putin Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in b23afe4.

I tightened the test_inspect_storage_rex4_slot_miss_inserts_and_returns_db_value regression test to use the existing storage_calls counter on the fixture:

  • capture the call count after the first miss/insert
  • assert the second inspect_storage on the same slot leaves that count unchanged

That makes the test actually prove the Entry::Occupied path is serving the cached slot rather than silently re-querying the DB.

Re-ran:

  • cargo fmt --all
  • cargo test -p mega-evm --lib inspect_storage -- --nocapture
  • cargo fmt --all --check
  • cargo check
  • cargo clippy --workspace --lib --examples --tests --benches --all-features --locked

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review update

  • Reviewed changes since prior review (commits 077a968, 0c53070, b23afe4).
  • The new storage_calls counter + second-call assertion in test_inspect_storage_rex4_slot_miss_... resolves my earlier cache-hit concern; that thread is now resolved (discussion r3569554901).
  • flyq's two open threads look addressed by this round and can be closed:
    • format-fix thread (discussion r3535250731) — CI lint now passes.
    • load-bearing "why" comment (discussion r3535258662) — stateless-replay / witness-lookup rationale is restored above the prologue at host.rs:502-506.
  • One small new finding on test naming, inline. No blocking concerns.

Comment thread crates/mega-evm/src/evm/host.rs
@mega-putin

mega-putin Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 6257b0b.

I took the stronger option and extended test_inspect_storage_pre_rex4_uses_delegation_path so it now really covers the pre-REX4 delegation behavior instead of just an absent-slot ZERO case.

The updated test:

  • uses with_eip7702_delegation(ADDR, DELEGATE)
  • preloads the delegator so the occupied-branch hydration exposes the delegation designation in this lazy-code fixture
  • seeds the storage slot on DELEGATE
  • asserts inspect_storage(MINI_REX, ADDR, key) returns the delegate’s slot value
  • asserts no DB storage lookup occurs

That also makes the divergence versus test_inspect_storage_rex4_ignores_eip7702_delegation explicit.

Re-ran:

  • cargo fmt --all
  • cargo test -p mega-evm --lib inspect_storage -- --nocapture
  • cargo fmt --all --check
  • cargo check
  • cargo clippy --workspace --lib --examples --tests --benches --all-features --locked

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧭 Re-review update

  • Scope: only new commit since my last review (6257b0b) — the pre-REX4 delegation test now seeds the DELEGATE's storage, asserts the returned slot value comes from DELEGATE, and pins storage_calls() == 0 to prove the delegate walk was actually followed (rather than falling through to a DB miss on the delegator). This closes my prior thread on the test not exercising delegation.
  • No new actionable findings.

Still open from earlier reviews:

@mega-putin

mega-putin Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

There’s no new actionable feedback on 6257b0b.

I verified the remaining reviewer suggestion locally:

  • cargo fmt --all --check passes on 6257b0b06e9df4a2f66246301832a7252599bb2b
  • the worktree is clean

I did not make or commit any additional changes, and there’s no reply comment required unless you want to post an optional acknowledgment.

@RealiCZ RealiCZ merged commit 68baa97 into main Jul 14, 2026
29 checks passed
@RealiCZ RealiCZ deleted the aro/perf-rex4-sload-entry-api branch July 14, 2026 06:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Generated by AI agents api:unchanged No change to the public interface or API comp:core Changes to the `mega-evm` core crate rust Pull requests that update rust code spec:stable Touches stable spec code — must not change behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants