Skip to content

Conversation

ilitteri
Copy link
Contributor

@ilitteri ilitteri commented Sep 23, 2025

Motivation

The original is_blacklisted function in JumpTargetFilter was a performance bottleneck, consuming ~30% of execution time during mainnet blocks stateless execution.

Description

Inspired by Geth's codeBitmap approach, this PR:

  1. Replaces incremental iteration with a one-time precompute method that scans the entire bytecode, building a BitVec<u8, Msb0> where bits mark valid JUMPDEST positions, skipping PUSH1..PUSH32 data bytes.
  2. Updates is_blacklisted to O(1) bit lookup: address >= bytecode.len() || !jumpdests[address].

Copy link

github-actions bot commented Sep 23, 2025

Lines of code report

Total lines added: 1
Total lines removed: 0
Total lines changed: 1

Detailed view
+------------------------------------+-------+------+
| File                               | Lines | Diff |
+------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/utils.rs | 427   | +1   |
+------------------------------------+-------+------+

Copy link

github-actions bot commented Sep 23, 2025

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 4.822 ± 0.073 4.778 5.018 1.05 ± 0.02
main_levm_BubbleSort 4.603 ± 0.009 4.587 4.615 1.00
pr_revm_BubbleSort 4.777 ± 0.023 4.760 4.838 1.04 ± 0.01
pr_levm_BubbleSort 4.633 ± 0.049 4.588 4.727 1.01 ± 0.01

Benchmark Results: ERC20Approval

Command Mean [s] Min [s] Max [s] Relative
main_revm_ERC20Approval 1.554 ± 0.009 1.543 1.566 1.01 ± 0.01
main_levm_ERC20Approval 1.655 ± 0.020 1.638 1.706 1.08 ± 0.01
pr_revm_ERC20Approval 1.539 ± 0.005 1.533 1.550 1.00
pr_levm_ERC20Approval 1.642 ± 0.013 1.622 1.665 1.07 ± 0.01

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 185.1 ± 0.7 184.2 186.5 1.01 ± 0.01
main_levm_ERC20Mint 196.1 ± 1.4 194.4 199.0 1.07 ± 0.01
pr_revm_ERC20Mint 183.6 ± 1.1 182.6 185.7 1.00
pr_levm_ERC20Mint 197.5 ± 4.4 195.2 210.0 1.08 ± 0.02

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 351.6 ± 1.3 350.4 354.9 1.00
main_levm_ERC20Transfer 387.7 ± 5.7 381.3 400.8 1.10 ± 0.02
pr_revm_ERC20Transfer 352.6 ± 5.6 348.8 363.1 1.00 ± 0.02
pr_levm_ERC20Transfer 385.5 ± 3.4 381.7 393.2 1.10 ± 0.01

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 237.4 ± 0.7 235.8 238.3 1.00
main_levm_Factorial 277.2 ± 3.1 274.7 285.4 1.17 ± 0.01
pr_revm_Factorial 238.4 ± 4.3 235.7 250.6 1.00 ± 0.02
pr_levm_Factorial 279.5 ± 6.5 275.1 297.3 1.18 ± 0.03

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.609 ± 0.021 1.573 1.636 1.01 ± 0.02
main_levm_FactorialRecursive 8.566 ± 0.035 8.484 8.599 5.37 ± 0.09
pr_revm_FactorialRecursive 1.596 ± 0.027 1.526 1.623 1.00
pr_levm_FactorialRecursive 8.523 ± 0.073 8.429 8.620 5.34 ± 0.10

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 216.2 ± 1.9 211.2 217.8 1.00
main_levm_Fibonacci 271.1 ± 2.0 269.2 275.9 1.25 ± 0.01
pr_revm_Fibonacci 216.6 ± 2.7 213.6 223.4 1.00 ± 0.02
pr_levm_Fibonacci 268.1 ± 4.4 259.1 274.0 1.24 ± 0.02

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 856.5 ± 12.6 832.2 872.1 1.00
main_levm_FibonacciRecursive 939.3 ± 8.1 932.9 956.1 1.10 ± 0.02
pr_revm_FibonacciRecursive 860.9 ± 6.5 847.3 869.7 1.01 ± 0.02
pr_levm_FibonacciRecursive 928.1 ± 10.6 918.3 950.8 1.08 ± 0.02

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 12.3 ± 0.0 12.2 12.4 1.00
main_levm_ManyHashes 13.6 ± 0.1 13.5 13.9 1.11 ± 0.01
pr_revm_ManyHashes 12.3 ± 0.1 12.2 12.6 1.00 ± 0.01
pr_levm_ManyHashes 13.6 ± 0.0 13.5 13.7 1.11 ± 0.01

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 273.2 ± 2.2 270.8 277.5 1.00
main_levm_MstoreBench 329.1 ± 0.8 328.0 330.4 1.20 ± 0.01
pr_revm_MstoreBench 273.8 ± 3.1 270.0 279.3 1.00 ± 0.01
pr_levm_MstoreBench 331.1 ± 4.4 327.6 342.9 1.21 ± 0.02

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 301.2 ± 5.1 297.9 315.7 1.00 ± 0.02
main_levm_Push 381.7 ± 4.6 373.6 389.8 1.27 ± 0.02
pr_revm_Push 299.9 ± 0.9 298.5 301.4 1.00
pr_levm_Push 380.0 ± 3.7 375.9 386.1 1.27 ± 0.01

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 217.1 ± 0.8 216.4 219.0 2.43 ± 0.08
main_levm_SstoreBench_no_opt 90.5 ± 3.5 86.6 97.8 1.01 ± 0.05
pr_revm_SstoreBench_no_opt 217.2 ± 0.4 216.6 217.7 2.43 ± 0.08
pr_levm_SstoreBench_no_opt 89.5 ± 2.8 86.6 96.3 1.00

Copy link

github-actions bot commented Sep 23, 2025

Benchmark Block Execution Results Comparison Against Main

Command Mean [s] Min [s] Max [s] Relative
base 87.399 ± 0.232 86.977 87.748 1.05 ± 0.01
head 82.894 ± 0.371 82.445 83.472 1.00

@ilitteri ilitteri marked this pull request as ready for review September 23, 2025 16:53
@ilitteri ilitteri requested a review from a team as a code owner September 23, 2025 16:53
@Copilot Copilot AI review requested due to automatic review settings September 23, 2025 16:53
Copy link
Contributor

@Copilot 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

This PR optimizes the JumpTargetFilter implementation to address a performance bottleneck where the original is_blacklisted function consumed ~30% of execution time during mainnet block execution.

  • Replaces incremental iteration with precomputed bitmap approach using BitVec<u8, Msb0>
  • Changes is_blacklisted from potentially O(n) to O(1) lookup operation
  • Adopts Geth's codeBitmap strategy for identifying valid JUMPDEST positions

Reviewed Changes

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

File Description
crates/vm/levm/src/utils.rs Refactors JumpTargetFilter to use bitmap-based jumpdest tracking instead of incremental filtering
crates/vm/levm/Cargo.toml Adds bitvec dependency for bitmap functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

jumpdests.set(i, true);
} else if (Opcode::PUSH1..=Opcode::PUSH32).contains(&opcode) {
// PUSH1 (0x60) to PUSH32 (0x7f): skip 1 to 32 bytes
let skip = opcode as usize - Opcode::PUSH0 as usize;
Copy link
Preview

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

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

The skip calculation is incorrect. For PUSH1 through PUSH32, the number of data bytes to skip should be opcode as usize - Opcode::PUSH1 as usize + 1. Currently, using PUSH0 as the base means PUSH1 skips 1 byte, PUSH2 skips 2 bytes, etc., but it should skip 1, 2, 3... up to 32 bytes respectively.

Suggested change
let skip = opcode as usize - Opcode::PUSH0 as usize;
let skip = opcode as usize - Opcode::PUSH1 as usize + 1;

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@JereSalo JereSalo left a comment

Choose a reason for hiding this comment

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

Why not just check if it's a jumpdest rather than checking that it's not blacklisted now that we have a bitvec with jumpdests? It feels more straightforward and less complex but I don't know if the other approach has more benefits

@edg-l
Copy link
Contributor

edg-l commented Sep 29, 2025

image

old gas bench looks good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
levm Lambda EVM implementation performance
Projects
Status: No status
Status: Todo
Development

Successfully merging this pull request may close these issues.

3 participants