Skip to content

feat(benchmarks): add BAL benchmarks for the optimization strategies introduced by BALs#2197

Merged
fselmo merged 14 commits intoethereum:forks/amsterdamfrom
jochem-brouwer:bal-benchmarks
Mar 17, 2026
Merged

feat(benchmarks): add BAL benchmarks for the optimization strategies introduced by BALs#2197
fselmo merged 14 commits intoethereum:forks/amsterdamfrom
jochem-brouwer:bal-benchmarks

Conversation

@jochem-brouwer
Copy link
Copy Markdown
Member

@jochem-brouwer jochem-brouwer commented Feb 12, 2026

🗒️ Description

This PR adds BAL tests, for more information and background see jochem-brouwer#1

Huge thanks to @fselmo for refactoring and adding tests!

🔗 Related Issues or PRs

N/A.

✅ Checklist

  • All: Ran fast tox checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    uvx tox -e static
  • All: PR title adheres to the repo standard - it will be used as the squash commit message and should start type(scope):.
  • All: Considered updating the online docs in the ./docs/ directory.
  • All: Set appropriate labels for the changes (only maintainers can apply labels).
  • Tests: Ran mkdocs serve locally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.
  • Tests: For PRs implementing a missed test case, update the post-mortem document to add an entry the list.
  • Ported Tests: All converted JSON/YML tests from ethereum/tests or tests/static have been assigned @ported_from marker.

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

@fselmo fselmo self-assigned this Feb 17, 2026
@fselmo fselmo self-requested a review February 17, 2026 14:40
- test_prefetch_cold_storage: Cold SLOAD workload with sequential and
  hash-chain scattered access patterns. The scattered pattern is
  unpredictable without BAL but trivially prefetchable with one.
- test_coinbase_serialization: Disjoint contracts where coinbase fee
  accumulation is the only shared state. Implicit (fees) and explicit
  (CALL to coinbase) variants.
- test_deploy_then_interact: Deploy/call tx pairs in a single block.
  Independent pairs (parallelizable) and single-contract (serial)
  variants.
- test_mixed_dependency_graph: Interleaved groups of serial keccak
  chains. Group sizes 1/2/5 control available parallelism.
@LouisTsai-Csie
Copy link
Copy Markdown
Collaborator

Hi @jochem-brouwer, based on your BAL benchmark description, I put together a small refactor below. I’ve run it locally and it passes.

@pytest.mark.valid_from("Amsterdam")
def test_tx_dependency(
    benchmark_test: BenchmarkTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Benchmark BAL with transaction-dependent execution.

    Deploy a contract that reads storage slot 0, computes a
    keccak256 hash chain until gas is nearly exhausted, and writes
    the result back. Each transaction depends on the previous
    one's storage write, preventing parallel execution.
    """
    target_slot = 0

    setup = Op.MSTORE(
        0,
        Op.SLOAD(target_slot, key_warm=False),
        # gas accounting
        old_memory_size=0,
        new_memory_size=32,
    )

    loop_body = Op.MSTORE(
        0,
        Op.SHA3(0, 32, data_size=32),
        # gas accounting
        old_memory_size=32,
        new_memory_size=32,
    )

    cleanup = (
        Op.SSTORE(
            target_slot,
            Op.MLOAD(0),
            # gas accounting
            key_warm=True,
            original_value=1,
            current_value=1,
            new_value=2,
        )
    )

    reserve_gas = cleanup.gas_cost(fork) + 50
    condition = Op.GT(Op.GAS, reserve_gas)

    attack_code = setup + While(body=loop_body, condition=condition) + cleanup

    attack_contract = pre.deploy_contract(
        code=attack_code,
        storage={0: 1},
    )

    benchmark_test(
        tx=Transaction(
            to=attack_contract,
            sender=pre.fund_eoa(),
        ),
        skip_gas_used_validation=True,
    )

Each transaction starts by reading slot 0, continues the hash chain, and then stores the final result back to slot 0. I didn’t change the overall design, but I simplified the per-transaction logic since our benchmark test wrapper already generates multiple transactions while respecting tx gas limit cap.

@fselmo
Copy link
Copy Markdown
Contributor

fselmo commented Feb 24, 2026

@LouisTsai-Csie can you please help review jochem-brouwer#1 when you get a chance 👀? It builds on the TODOs here and adds more test cases. If it's not far off and only needs tweaks, I think we can at least merge it and work on subsequent updates here so that we have a more complete picture PR'd to ethereum/execution-specs, and since this is still in Draft. Wdyt?


condition = Op.GT(Op.GAS, reserve_gas)

loop = While(body=keccak_body, condition=condition)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

issue(perf, non-blocking): Loop wastes 5 gas per cycle

The while loop compiles to:

 [setup][JUMPDEST][body][condition][compute jumpdest][JUMPI][cleanup]

and the jumpdest at runtime using an offset:

 JUMPDESTwe want to jump back here         
    body                                                                
    condition
    PUSH4 <offset>"how far back is the JUMPDEST?" 
    PC"where am I right now?"  
    SUBPC - offset = JUMPDEST address                 
    JUMPI                                                  

So PC + SUB (= 5 gas) is a workaround ["I don't know where I am in absolute terms, but I know how far back to jump."]

i.e: the root cause is: While is not aware of setup.

Suggestion

Introduce an optional setup code for While generator ( = for loop primitive) so jumpdest can be computed at compile-time JUMPDEST = len(setup):

  setup
  JUMPDEST          
    body
    condition
    PUSH1  len(setup)    
    JUMPI

cc: @marioevz @LouisTsai-Csie

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a good point and it looks like this is part of the definition of While. I think it's possible to generalize this idea and thus to remove the calculate-jumpdest-at-runtime for all while loops. (Should be addressed in a refactor at some point)

@jochem-brouwer jochem-brouwer changed the title feat(tests): add hash chain test to test parallel execution benchs feat(benchmarks): add BAL benchmarks for the optimization strategies introduced by BALs Mar 17, 2026
@jochem-brouwer jochem-brouwer marked this pull request as ready for review March 17, 2026 00:28
@jochem-brouwer
Copy link
Copy Markdown
Member Author

I have tested against EthJS to verify that the tests fill and added some minor changes. The tests fill, and on a quick inspection it also looks like they execute the expected behavior (loops without OOGs).

I think we can merge this one for BAL-specific benchmarks.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.01%. Comparing base (be3678d) to head (2dfb2fb).
⚠️ Report is 170 commits behind head on forks/amsterdam.

Additional details and impacted files
@@                 Coverage Diff                 @@
##           forks/amsterdam    #2197      +/-   ##
===================================================
- Coverage            86.07%   86.01%   -0.07%     
===================================================
  Files                  599      599              
  Lines                39472    36904    -2568     
  Branches              3780     3771       -9     
===================================================
- Hits                 33977    31744    -2233     
+ Misses                4862     4551     -311     
+ Partials               633      609      -24     
Flag Coverage Δ
unittests 86.01% <ø> (-0.07%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

Comment on lines +73 to +76
body_gas = body.gas_cost(fork)
placeholder = Op.GT(Op.GAS, Op.PUSH1(0))
per_iter_gas = While(body=body, condition=placeholder).gas_cost(fork)
exit_overhead = per_iter_gas - body_gas - Op.JUMPDEST.gas_cost(fork)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just leave a note here, i hope PR #2103 could help when it is merged

Copy link
Copy Markdown
Contributor

@fselmo fselmo left a comment

Choose a reason for hiding this comment

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

This lgtm and we should ideally get this in now so going to merge :). @LouisTsai-Csie you mentioned you'd like to refactor here. Can you create an issue to track the refactor? Going to ping you here so you do not forget :)

@fselmo fselmo merged commit 905db26 into ethereum:forks/amsterdam Mar 17, 2026
14 checks passed
morph-dev pushed a commit to morph-dev/execution-specs that referenced this pull request Mar 18, 2026
* ✨ feat(tests): EIP-7928 SELFDESTRUCT tests
* feat: point to latest commit in BALs specs (resolver)
* feat: Validate t8n BAL does not have duplicate entries for the same tx_index
* 📄 docs: Changelog entry
* chore: avoid extra fields in BAL classes, related to ethereum#2197
* Add tests for EIP-7928 around precompiles (doc)
* fix(tests): Fix expectations for self-destruct tests

---------

Co-authored-by: raxhvl <raxhvl@users.noreply.github.com>
Co-authored-by: fselmo <fselmo2@gmail.com>
Co-authored-by: Toni Wahrstätter <51536394+nerolation@users.noreply.github.com>
flcl42 pushed a commit to flcl42/execution-specs that referenced this pull request Apr 10, 2026
* ✨ feat(tests): EIP-7928 SELFDESTRUCT tests
* feat: point to latest commit in BALs specs (resolver)
* feat: Validate t8n BAL does not have duplicate entries for the same tx_index
* 📄 docs: Changelog entry
* chore: avoid extra fields in BAL classes, related to ethereum#2197
* Add tests for EIP-7928 around precompiles (doc)
* fix(tests): Fix expectations for self-destruct tests

---------

Co-authored-by: raxhvl <raxhvl@users.noreply.github.com>
Co-authored-by: fselmo <fselmo2@gmail.com>
Co-authored-by: Toni Wahrstätter <51536394+nerolation@users.noreply.github.com>
flcl42 pushed a commit to flcl42/execution-specs that referenced this pull request Apr 10, 2026
* ✨ feat(tests): EIP-7928 SELFDESTRUCT tests
* feat: point to latest commit in BALs specs (resolver)
* feat: Validate t8n BAL does not have duplicate entries for the same tx_index
* 📄 docs: Changelog entry
* chore: avoid extra fields in BAL classes, related to ethereum#2197
* Add tests for EIP-7928 around precompiles (doc)
* fix(tests): Fix expectations for self-destruct tests

---------

Co-authored-by: raxhvl <raxhvl@users.noreply.github.com>
Co-authored-by: fselmo <fselmo2@gmail.com>
Co-authored-by: Toni Wahrstätter <51536394+nerolation@users.noreply.github.com>
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.

4 participants