Skip to content

feat(l1): add --no-precompile-cache flag for benchmarking#6562

Open
ilitteri wants to merge 1 commit intomainfrom
feat/no-precompile-cache-flag
Open

feat(l1): add --no-precompile-cache flag for benchmarking#6562
ilitteri wants to merge 1 commit intomainfrom
feat/no-precompile-cache-flag

Conversation

@ilitteri
Copy link
Copy Markdown
Collaborator

@ilitteri ilitteri commented May 1, 2026

Motivation

The per-block precompile result cache (PrecompileCache in crates/vm/levm/src/precompiles.rs) is always on during block execution. To benchmark how much the cache contributes to block-execution time, we need an opt-in switch to disable it without otherwise altering the execution pipeline.

Description

Adds a --no-precompile-cache CLI flag (env ETHREX_NO_PRECOMPILE_CACHE, default off) that disables the per-block precompile result cache for the full-node entry point and the import-bench subcommand. Default behavior is preserved everywhere else.

  • BlockchainOptions gains a new precompile_cache_enabled: bool field (defaults to true), which Blockchain::execute_block_pipeline passes into CachingDatabase::new.
  • CachingDatabase::precompile_cache becomes Option<PrecompileCache> so that, when disabled, no cache is allocated. Database::precompile_cache() returns self.precompile_cache.as_ref(). execute_precompile already short-circuits cleanly on None.
  • Wired only at two BlockchainOptions construction sites:
    • cmd/ethrex/initializers.rs — full-node startup
    • cmd/ethrex/cli.rsSubcommand::ImportBench
  • Other entry points (plain import, L2, ef-tests, migrations, state_v2, benches) keep their existing cached behavior via Default.

Files changed

File Change
crates/vm/levm/src/db/mod.rs CachingDatabase::precompile_cache is now Option<PrecompileCache>; new takes precompile_cache_enabled: bool; trait impl returns as_ref()
crates/blockchain/blockchain.rs New precompile_cache_enabled: bool on BlockchainOptions (default true); passed to CachingDatabase::new in execute_block_pipeline
cmd/ethrex/cli.rs New --no-precompile-cache clap arg; Subcommand::ImportBench reads it
cmd/ethrex/initializers.rs Full-node BlockchainOptions reads !opts.no_precompile_cache
cmd/ethrex/l2/initializers.rs Adds precompile_cache_enabled: true (struct literal spelled exhaustively; behavior unchanged — L2 keeps cache on)

Usage

# baseline (cache on, default)
ethrex import-bench /path/to/blocks.rlp

# disabled (for A/B benchmarking)
ethrex --no-precompile-cache import-bench /path/to/blocks.rlp

# or via env
ETHREX_NO_PRECOMPILE_CACHE=true ethrex …

The flag sits on the parent Options struct, so it goes before the subcommand (mirroring --no-migrate).

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync. — N/A, no Store changes.

Adds a CLI flag (env ETHREX_NO_PRECOMPILE_CACHE, default off) that disables
the per-block precompile result cache for the full-node entry point and the
import-bench subcommand, leaving every other call site cached by default.

Implementation: BlockchainOptions.precompile_cache_enabled (default true) is
threaded into CachingDatabase, which now stores Option<PrecompileCache> so
nothing is allocated when disabled. Database::precompile_cache() returns
self.precompile_cache.as_ref(); execute_precompile already handles None.
Copilot AI review requested due to automatic review settings May 1, 2026 13:14
@ilitteri ilitteri requested review from a team, ManuelBilbao and avilagaston9 as code owners May 1, 2026 13:14
@github-actions github-actions Bot added the L1 Ethereum client label May 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

🤖 Kimi Code Review

General Assessment
The PR correctly implements a toggle for the per-block precompile cache, allowing it to be disabled for benchmarking. The implementation is memory-safe and thread-safe, properly using Option<PrecompileCache> to eliminate cache overhead when disabled.

Specific Issues

  1. L2 Configuration Inconsistency (cmd/ethrex/l2/initializers.rs:222)
    The precompile cache is hardcoded to true for L2 nodes, while L1 respects the --no-precompile-cache CLI flag. If L2 benchmarking requires disabling this cache, the flag should be threaded through to L2 options, or a comment should explain why L2 always requires caching.

  2. Documentation Grammar (crates/blockchain/blockchain.rs:230-233)
    The doc comment is slightly unclear. Suggested revision:

    /// If true (default), enables per-block caching of precompile results shared between
    /// the warmer thread and the executor. Set to false (via `--no-precompile-cache`) to
    /// disable the cache for benchmarking purposes.
    
  3. API Breaking Change (crates/vm/levm/src/db/mod.rs)
    The inherent method pub fn precompile_cache(&self) -> &PrecompileCache was removed in favor of the trait method returning Option<&PrecompileCache>. While this is correct for the new behavior, it is a breaking change for any external crates using CachingDatabase directly. Ensure this is noted in the changelog, or consider deprecating the method first.

  4. Comment Clarity (crates/vm/levm/src/db/mod.rs:64-65)
    The comment explaining precompile_cache could be more explicit:

    /// Shared precompile result cache (warmer populates, executor reuses).
    /// `None` when disabled via `BlockchainOptions::precompile_cache_enabled`.
    

Positive Feedback

  • Correct use of bool::then for conditional initialization (crates/vm/levm/src/db/mod.rs:71).
  • Proper propagation of the configuration flag through BlockchainOptions to CachingDatabase.
  • The Option<&PrecompileCache> return type in the trait implementation correctly forces callers to handle the disabled case.

Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

🤖 Claude Code Review

Now I have a complete picture of the changes. Let me write the review.


PR Review: feat(l1): add --no-precompile-cache flag for benchmarking

Summary

This is a clean, focused PR. The approach — making PrecompileCache optional via Option<PrecompileCache> — is idiomatic Rust and the changes are minimal and well-contained.


Correctness

bool::then(PrecompileCache::new) in db/mod.rs:76

This is correct and idiomatic. bool::then calls the closure only when true, yielding Some(val) or None. Works cleanly as long as PrecompileCache::new is a zero-argument constructor (which it appears to be).

Trait impl returning self.precompile_cache.as_ref() (db/mod.rs:179)

Correct. The return type Option<&PrecompileCache> is now naturally satisfied by the field's Option type.

Removing the inherent precompile_cache() method

Clean removal — the trait method now does the right thing.


Potential Issues

Silent no-op for L2 + env var (l2/initializers.rs:222)

precompile_cache_enabled: true,  // hardcoded

If a user sets ETHREX_NO_PRECOMPILE_CACHE=true and starts an L2 node, the env var is silently ignored. This isn't a bug per se (the PR intentionally keeps L2 always cached), but it's a latent footgun and UX surprise. Worth adding either:

  • A warn!() log line in init_l2 if opts.node_opts.no_precompile_cache is true, or
  • A note in the help text like "(L1 and import-bench only)".

--no-precompile-cache is a global flag but only wired to two code paths

The flag lives on Options (global), but only init_l1 and ImportBench read it. Other subcommands that construct BlockchainOptions without ..Default::default() will silently use the cached default even if the flag is set. This appears intentional per the PR description, but isn't communicated to the user via CLI help text.


Minor Notes

CachingDatabase::new bool parameter (db/mod.rs:67)

A bare bool works for this use case, but a named parameter or even a tiny enum (PrecompileCacheMode::Enabled/Disabled) would make call-sites self-documenting. That said, given this is a benchmarking-only concern touching few call sites, the bool is pragmatically fine — not requesting a change, just flagging the tradeoff.

Doc comment verbosity (blockchain.rs:230-233)

/// If true (default), per-block execution caches precompile results between the
/// warmer thread and the executor. Set to false (via `--no-precompile-cache`) to
/// disable the cache for benchmarking purposes.

The mention of "warmer thread and the executor" leaks implementation internals into what should be an options-level description. A one-liner like /// If true (default), enables per-block precompile result caching. Disable via \--no-precompile-cache` for benchmarking.` would be cleaner.

precompile_cache_enabled: true in l2/initializers.rs is explicit but redundant

The Default impl already sets this to true. The explicit value is needed here because the struct is constructed without ..Default::default(), so it's a compile requirement — this is correct. No change needed.


Security

No security concerns. The cache is a performance optimization with no consensus impact; disabling it cannot cause incorrect execution, only slower execution.


Overall

The implementation is correct, idiomatic, and minimal in scope. The main actionable point is the silent no-op when --no-precompile-cache is set for L2 mode — a warn!() log or updated help text would prevent user confusion. Everything else is minor style.


Automated review by Claude (Anthropic) · sonnet · custom prompt

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Lines of code report

Total lines added: 15
Total lines removed: 3
Total lines changed: 18

Detailed view
+----------------------------------------+-------+------+
| File                                   | Lines | Diff |
+----------------------------------------+-------+------+
| ethrex/cmd/ethrex/cli.rs               | 994   | +10  |
+----------------------------------------+-------+------+
| ethrex/cmd/ethrex/initializers.rs      | 649   | +1   |
+----------------------------------------+-------+------+
| ethrex/cmd/ethrex/l2/initializers.rs   | 364   | +1   |
+----------------------------------------+-------+------+
| ethrex/crates/blockchain/blockchain.rs | 2490  | +3   |
+----------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/db/mod.rs    | 145   | -3   |
+----------------------------------------+-------+------+

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 1, 2026

Greptile Summary

This PR adds a --no-precompile-cache CLI flag (and ETHREX_NO_PRECOMPILE_CACHE env var) that disables the per-block precompile result cache in CachingDatabase for benchmarking purposes. The implementation is clean: precompile_cache becomes Option<PrecompileCache> in CachingDatabase, execute_precompile already handles None via cache.and_then(...) guards, and default behavior (cache enabled) is preserved for all existing entry points through BlockchainOptions::default().

Confidence Score: 5/5

Safe to merge — opt-in benchmarking flag with no behavior change on the default path.

No P0 or P1 issues found. The None-cache path in execute_precompile is correctly guarded with cache.and_then/let Some(cache) patterns, all call sites are updated, default remains cache-on, and both exhaustive struct literals compile correctly with the new field.

No files require special attention.

Important Files Changed

Filename Overview
crates/vm/levm/src/db/mod.rs Changes precompile_cache field to Option, using .then(PrecompileCache::new) for conditional allocation; precompile_cache() trait impl correctly returns self.precompile_cache.as_ref()
crates/blockchain/blockchain.rs Adds precompile_cache_enabled: bool to BlockchainOptions (default true) and passes it to CachingDatabase::new; exhaustive struct literal in init_l1 covers all fields
cmd/ethrex/cli.rs Adds --no-precompile-cache clap arg (env ETHREX_NO_PRECOMPILE_CACHE) to Options; wired to BlockchainOptions for ImportBench; Default impl updated correctly
cmd/ethrex/initializers.rs Passes !opts.no_precompile_cache to BlockchainOptions for full-node startup; exhaustive struct literal remains complete with all fields
cmd/ethrex/l2/initializers.rs Explicitly sets precompile_cache_enabled: true to keep existing L2 behavior; updated exhaustive struct literal compiles cleanly

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["CLI: --no-precompile-cache\n(ETHREX_NO_PRECOMPILE_CACHE)"] --> B{Options.no_precompile_cache}
    B -->|Full node init_l1| C["BlockchainOptions\nprecompile_cache_enabled = !no_precompile_cache"]
    B -->|ImportBench subcommand| D["BlockchainOptions\nprecompile_cache_enabled = !no_precompile_cache"]
    E["L2 init_l2\n(always true)"] --> F["BlockchainOptions\nprecompile_cache_enabled = true"]
    G["Other entry points\n(import, ef-tests, benches...)"] --> H["BlockchainOptions::default()\nprecompile_cache_enabled = true"]
    C & D & F & H --> I["Blockchain::execute_block_pipeline"]
    I --> J["CachingDatabase::new(store, precompile_cache_enabled)"]
    J -->|true| K["precompile_cache: Some(PrecompileCache::new())"]
    J -->|false| L["precompile_cache: None"]
    K & L --> M["execute_precompile(...)"]
    M -->|Some cache| N["cache.get / cache.insert\n(fast path)"]
    M -->|None cache| O["compute directly\n(no cache read/write)"]
Loading

Reviews (1): Last reviewed commit: "feat(l1): add --no-precompile-cache flag..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

🤖 Codex Code Review

  1. cmd/ethrex/l2/initializers.rs:216-223 hardcodes precompile_cache_enabled: true, so the new --no-precompile-cache / ETHREX_NO_PRECOMPILE_CACHE toggle is ignored on the main L2 startup path. L2Options flattens node_opts (cmd/ethrex/l2/options.rs:31-35), so the flag is accepted by the CLI but has no effect there. That makes L2 benchmarking misleading and creates inconsistent behavior versus L1 and import-bench. This should mirror L1 and use !opts.node_opts.no_precompile_cache.

Aside from that wiring bug, the Option<PrecompileCache> change in crates/vm/levm/src/db/mod.rs looks sound: it degrades to recomputation rather than changing EVM semantics, gas accounting, or validation behavior.

I couldn’t complete a local cargo check in this environment because rustup tried to write under /home/runner/.rustup, which is read-only here.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in CLI switch to disable the per-block precompile result cache to enable A/B benchmarking of block execution performance, while keeping the default cached behavior unchanged for existing entry points.

Changes:

  • Introduces --no-precompile-cache / ETHREX_NO_PRECOMPILE_CACHE and wires it into L1 full-node startup and import-bench.
  • Threads a new precompile_cache_enabled flag through BlockchainOptions into CachingDatabase.
  • Makes CachingDatabase’s precompile cache optional to avoid allocating it when disabled.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/vm/levm/src/db/mod.rs Makes precompile_cache optional and updates CachingDatabase construction / trait exposure accordingly.
crates/blockchain/blockchain.rs Adds precompile_cache_enabled to BlockchainOptions (default true) and passes it into CachingDatabase.
cmd/ethrex/cli.rs Adds the --no-precompile-cache CLI flag/env var; applies it for ImportBench.
cmd/ethrex/initializers.rs Applies the flag to L1 full-node BlockchainOptions.
cmd/ethrex/l2/initializers.rs Explicitly keeps the cache enabled for L2 initialization (behavior unchanged).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// Shared precompile result cache (warmer populates, executor reuses)
precompile_cache: PrecompileCache,
/// Shared precompile result cache (warmer populates, executor reuses).
/// `None` when the cache is disabled via `BlockchainOptions::precompile_cache_enabled = false`.
Comment on lines +69 to 73
pub fn new(inner: Arc<dyn Database>, precompile_cache_enabled: bool) -> Self {
Self {
inner,
accounts: RwLock::new(FxHashMap::default()),
storage: RwLock::new(FxHashMap::default()),
Comment on lines +230 to +233
/// If true (default), per-block execution caches precompile results between the
/// warmer thread and the executor. Set to false (via `--no-precompile-cache`) to
/// disable the cache for benchmarking purposes.
pub precompile_cache_enabled: bool,
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Benchmark Results Comparison

No significant difference was registered for any benchmark run.

Detailed Results

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
main_revm_BubbleSort 3.244 ± 0.021 3.203 3.275 1.13 ± 0.01
main_levm_BubbleSort 2.880 ± 0.017 2.858 2.920 1.00
pr_revm_BubbleSort 3.223 ± 0.014 3.192 3.238 1.12 ± 0.01
pr_levm_BubbleSort 2.936 ± 0.125 2.877 3.290 1.02 ± 0.04

Benchmark Results: ERC20Approval

Command Mean [s] Min [s] Max [s] Relative
main_revm_ERC20Approval 1.061 ± 0.016 1.042 1.088 1.00 ± 0.03
main_levm_ERC20Approval 1.094 ± 0.012 1.075 1.110 1.03 ± 0.02
pr_revm_ERC20Approval 1.060 ± 0.021 1.035 1.088 1.00
pr_levm_ERC20Approval 1.100 ± 0.013 1.090 1.130 1.04 ± 0.02

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 140.5 ± 1.4 139.4 144.0 1.00
main_levm_ERC20Mint 155.1 ± 1.0 153.9 157.1 1.10 ± 0.01
pr_revm_ERC20Mint 143.7 ± 14.9 138.5 186.2 1.02 ± 0.11
pr_levm_ERC20Mint 156.1 ± 0.8 154.8 157.8 1.11 ± 0.01

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 248.1 ± 3.3 245.3 256.1 1.00
main_levm_ERC20Transfer 267.4 ± 1.8 264.4 271.0 1.08 ± 0.02
pr_revm_ERC20Transfer 248.8 ± 2.8 246.0 254.8 1.00 ± 0.02
pr_levm_ERC20Transfer 268.9 ± 2.3 266.1 273.1 1.08 ± 0.02

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 223.7 ± 3.2 221.9 232.8 1.00
main_levm_Factorial 251.9 ± 9.3 248.1 278.2 1.13 ± 0.04
pr_revm_Factorial 225.1 ± 6.6 220.9 243.5 1.01 ± 0.03
pr_levm_Factorial 251.0 ± 2.1 248.9 255.7 1.12 ± 0.02

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.677 ± 0.045 1.583 1.742 1.10 ± 0.03
main_levm_FactorialRecursive 1.518 ± 0.006 1.507 1.528 1.00
pr_revm_FactorialRecursive 1.635 ± 0.032 1.579 1.688 1.08 ± 0.02
pr_levm_FactorialRecursive 1.530 ± 0.024 1.510 1.579 1.01 ± 0.02

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 215.0 ± 7.0 207.3 230.2 1.02 ± 0.03
main_levm_Fibonacci 228.1 ± 6.6 222.4 246.0 1.08 ± 0.03
pr_revm_Fibonacci 211.3 ± 0.5 210.7 212.2 1.00
pr_levm_Fibonacci 226.5 ± 2.4 222.8 232.1 1.07 ± 0.01

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 866.6 ± 7.1 855.6 877.1 1.22 ± 0.02
main_levm_FibonacciRecursive 720.5 ± 34.8 698.6 809.3 1.02 ± 0.05
pr_revm_FibonacciRecursive 855.9 ± 13.6 836.0 881.4 1.21 ± 0.02
pr_levm_FibonacciRecursive 707.8 ± 9.2 696.2 726.3 1.00

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 9.0 ± 0.1 8.9 9.1 1.00 ± 0.02
main_levm_ManyHashes 10.6 ± 0.1 10.5 10.8 1.18 ± 0.02
pr_revm_ManyHashes 9.0 ± 0.1 8.9 9.3 1.00
pr_levm_ManyHashes 10.6 ± 0.1 10.4 10.7 1.17 ± 0.02

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 274.5 ± 5.7 269.9 289.3 1.17 ± 0.03
main_levm_MstoreBench 236.4 ± 3.7 234.2 246.3 1.00 ± 0.02
pr_revm_MstoreBench 273.5 ± 6.3 269.8 291.2 1.16 ± 0.03
pr_levm_MstoreBench 235.3 ± 1.8 233.4 239.6 1.00

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 314.2 ± 1.1 312.7 315.7 1.08 ± 0.01
main_levm_Push 290.5 ± 0.9 289.4 291.8 1.00
pr_revm_Push 315.0 ± 2.8 312.5 322.0 1.08 ± 0.01
pr_levm_Push 294.6 ± 1.6 292.7 298.1 1.01 ± 0.01

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 178.4 ± 9.7 172.6 205.1 1.59 ± 0.09
main_levm_SstoreBench_no_opt 112.5 ± 0.6 111.8 113.6 1.00
pr_revm_SstoreBench_no_opt 178.2 ± 8.1 172.3 199.7 1.58 ± 0.07
pr_levm_SstoreBench_no_opt 115.9 ± 4.5 113.0 126.2 1.03 ± 0.04

Copy link
Copy Markdown
Contributor

@pablodeymo pablodeymo left a comment

Choose a reason for hiding this comment

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

The code is righr t.

One nit, the new CLI arg should be documented in: docs/CLI.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants