perf(host): fold inspect_account calls + entry() API on REX4 SLOAD path (-5.84%, -2.03%)#334
Conversation
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
🧬 Mutation testing — ✅ PASSNothing to test — no mutants were generated on the changed lines. |
Codecov Report❌ Patch coverage is
☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
🧬 Mutation testing — ✅ PASSNothing to test — no mutants were generated (1 unviable, 0 timed out). |
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.
There was a problem hiding this comment.
- Summary: Reviewed the fold+entry SLOAD hot-path refactor in
inspect_storage. The fold from twoinspect_account(_,_,false)calls into oneinspect_account(_,_,true)preserves the DB-call sequence (occupied branch'scode_by_hashhydration + vacant branch'sbasic+ optionalcode_by_hash), so the final account state is byte-identical. Theget_mut(&address).unwrap()is safe because the prologue guaranteesinspect_accountinserted 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 theis_newly_createdshort-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 whoseLoadedcache status bypasses revm'sState::storageshort-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 flippingload_code=trueback tofalse. Not a blocker — this is a coverage nit that could be addressed later if needed.
|
Addressed both points in
Post-change verification:
|
…ss path Generated-by: engineer-agent
There was a problem hiding this comment.
🧭 Re-review update — ✅ Clean
- Reviewed the delta since the last review (
3e3e3b4..077a969): restored stateless-replay rationale on theis_newly_createdprologue, 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, soself.inner.state.get_mut(&address).unwrap()on the REX4 vacant/occupied branch is guaranteed safe; thedebug_assert!oncode_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):
- flyq — cargo fmt failures in new tests — all five flagged asserts now match rustfmt output.
- flyq — restore load-bearing why-comment for is_newly_created short-circuit — restored on the shared prologue at crates/mega-evm/src/evm/host.rs:502-506, so both the REX4 and pre-REX4 consumers inherit it.
Merging this PR will improve performance by 12.18%
Performance Changes
Tip Curious why this is faster? Comment Comparing |
|
I reviewed the new PR feedback. There’s no new actionable request in it:
The worktree is clean on |
There was a problem hiding this comment.
- Summary: The composed fold +
entry()API optimization on the REX4/REX5 SLOAD path looks correct — the borrow-narrowingget_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 --checklocally (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 sharedis_newly_createdprologue; leaving for the original human reviewer to resolve.
|
Addressed in I tightened the
That makes the test actually prove the Re-ran:
|
Generated-by: engineer-agent
There was a problem hiding this comment.
Re-review update
- Reviewed changes since prior review (commits
077a968,0c53070,b23afe4). - The new
storage_callscounter + second-call assertion intest_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
lintnow passes. - load-bearing "why" comment (discussion r3535258662) — stateless-replay / witness-lookup rationale is restored above the prologue at
host.rs:502-506.
- format-fix thread (discussion r3535250731) — CI
- One small new finding on test naming, inline. No blocking concerns.
|
Addressed in I took the stronger option and extended The updated test:
That also makes the divergence versus Re-ran:
|
Generated-by: engineer-agent
There was a problem hiding this comment.
🧭 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 pinsstorage_calls() == 0to 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:
- flyq:
cargo fmt --all --checkfailure — the flagged spots (host.rs:920/927/958/977/1007) all appear rewrapped in commit077a968; worth a quick localcargo fmt --all --checkto confirm CI is green before merging. - flyq: restore the deleted
is_newly_createdrationale — the stateless-replay rationale now lives on the prologue athost.rs:502-506, shared by both the REX4 and pre-REX4 paths. Looks addressed; flyq should confirm and close.
|
There’s no new actionable feedback on I verified the remaining reviewer suggestion locally:
I did not make or commit any additional changes, and there’s no reply comment required unless you want to post an optional acknowledgment. |
Summary
Two composed optimizations on the REX4/REX5 SLOAD hot path in
inspect_storage:Fold two
inspect_accountcalls into one (-5.84% ns_per_call): The old code calledinspect_account(self, address, false)twice — once foris_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 singleinspect_account(self, address, true)produces identical final account state with one fewerstate.entrylookup.entry()API for single HashMap probe (-2.03% ns_per_call): The oldcontains_key+ reload +get/insertpattern performs 2–3 HashMap lookups per SLOAD. Theentry()API reduces this to exactly one. The borrow-narrowing trick (self.inner.state.get_mut(&address).unwrap()instead ofinspect_account(self, address, false)?) keepsself.databasereachable for the miss path.The pre-REX4 path is unchanged in behavior and simplified in structure (no more
is_rex4_enabledbranches 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):pass-risk).unwrap()-based reloads without theentry()API), so this PR re-derives theentry()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'sinspect_account(self, address, true)?guarantees the account is inserted intoinner.stateon both occupied and vacant paths. Theunwrap()cannot fail under the current control flow. The codebase already uses this pattern ininspect_account_delegated's reloads.get_mut(&address).unwrap(), the borrow is oninner.stateonly, notself. The miss path callsself.database.storage(...)which is disjoint — no borrow conflict. Verified by the compiler.inspect_accountwithload_code=trueon the occupied path is byte-identical toload_code=falsefollowed by another occupiedload_code=false(both hydrate the same lazy code).entry()API is semantically equivalent tocontains_key+get/insert.is_rex4_enabledbranches 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)Entry::OccupiedEntry::Vacant+database.storageis_newly_createdshort-circuitmain(5a2ea61) using theevm_r3_diffprobe (2000 seeded transactions × 4 specs, FNV-1a fingerprint). Baseline and patched produce the same fingerprint:DIFF 211c3c3690873e3d.Verification
Re-verified on current
mainat baseline commit5a2ea61:This PR was generated by an automated agent. Campaign: ARO mega-evm-0703, edits a16/r0-2 + a20/r0-2 (re-derived).