Skip to content

bench: Add benchmark for block and Epoch Processing - #121

Merged
spiral-ladder merged 17 commits into
ChainSafe:mainfrom
guha-rahul:bench_block
Jan 10, 2026
Merged

bench: Add benchmark for block and Epoch Processing#121
spiral-ladder merged 17 commits into
ChainSafe:mainfrom
guha-rahul:bench_block

Conversation

@guha-rahul

Copy link
Copy Markdown
Contributor

tracks #102

  • Adds benchmark for block and epoch transition
  • fix a bug in getAttestationDataSigningRoot
  • Unlock spec tests to test for proposer and signatures in state transition which was probably failing because of the above issue

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @guha-rahul, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the performance testing suite by introducing detailed benchmarks for both block and epoch processing within the state transition logic. These new benchmarks will help in identifying performance bottlenecks and optimizing critical operations. Additionally, it includes a crucial bug fix in the attestation data signing root calculation, which in turn allowed for the re-enabling of proposer and signature verification in the state transition spec tests, improving test coverage and reliability. The Fulu fork epoch configuration was also updated.

Highlights

  • Block Processing Benchmarks: Introduced new benchmarks for individual process_block operations, such as header, withdrawals, execution payload, randao, eth1 data, operations, and sync aggregate, to measure their performance.
  • Epoch Processing Benchmarks: Added comprehensive benchmarks for all process_epoch operations, including justification, inactivity updates, rewards, registry updates, slashings, and more, to evaluate epoch transition performance.
  • Bug Fix in Attestation Data Signing Root: Corrected an issue in getAttestationDataSigningRoot by changing computeEpochAtSlot to computeStartSlotAtEpoch for calculating the slot, ensuring accurate attestation data signing roots.
  • Enabled Spec Test Verifications: Unlocked and enabled verification for proposer and signatures in state transition spec tests, which were previously disabled due to the bug now fixed, improving test coverage and reliability.
  • Fulu Fork Epoch Update: Updated the FULU_FORK_EPOCH configuration for the mainnet chain to its correct value of 411392.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces new benchmarks for process_block and process_epoch operations within the state transition logic, specifically for the Fulu fork. These benchmarks, located in bench/state_transition/process_block.zig and bench/state_transition/process_epoch.zig, load real mainnet state and block data from SSZ files to measure the performance of individual block and epoch processing steps. A critical configuration update was made in src/config/chain/networks/mainnet.zig, setting FULU_FORK_EPOCH to 411392 to enable the Fulu fork on the mainnet, which is essential for the new benchmarks and Fulu-related logic. A critical bug fix was also implemented in src/state_transition/signature_sets/indexed_attestation.zig, changing computeEpochAtSlot to computeStartSlotAtEpoch to ensure the getDomain function receives the correct Slot type for attestation data signing root calculation. To enhance test coverage and correctness, verify_proposer and verify_signatures were enabled in the spec tests (test/spec/runner/transition.zig), validating the signature verification mechanisms, especially after the bug fix. The build system (build.zig and zbuild.zon) was updated to include these new benchmark executables and their respective test targets.

.ELECTRA_FORK_EPOCH = 364032,
.FULU_FORK_VERSION = b(4, "0x06000000"),
.FULU_FORK_EPOCH = std.math.maxInt(u64),
.FULU_FORK_EPOCH = 411392,

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.

critical

Changing FULU_FORK_EPOCH from std.math.maxInt(u64) to a concrete value 411392 is a critical configuration update. This enables the Fulu fork on the mainnet configuration, which is essential for the new benchmarks and any Fulu-related logic to function correctly. This is a functional change, not a bug, and is necessary for the Fulu benchmarks to run correctly.


pub fn getAttestationDataSigningRoot(cached_state: *const CachedBeaconStateAllForks, data: *const AttestationData, out: *[32]u8) !void {
const slot = computeEpochAtSlot(data.target.epoch);
const slot = computeStartSlotAtEpoch(data.target.epoch);

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.

critical

This change from computeEpochAtSlot to computeStartSlotAtEpoch is a critical bug fix. The getDomain function expects a Slot type for its message_slot parameter, but data.target.epoch is an Epoch type. computeStartSlotAtEpoch correctly converts the epoch number to its starting slot number, ensuring the getDomain function receives the expected input. This directly addresses the bug mentioned in the PR description.

Comment on lines +132 to +133
.verify_proposer = true,
.verify_signatures = true,

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.

medium

Enabling verify_proposer and verify_signatures in the spec tests is a significant improvement for test coverage and correctness. This change ensures that the state transition logic is thoroughly validated, especially in light of the bug fix in getAttestationDataSigningRoot mentioned in the PR description. It indicates increased confidence in the correctness of the signature verification mechanisms.

Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_epoch.zig
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
@guha-rahul

Copy link
Copy Markdown
Contributor Author

@spiral-ladder zbench has a character limit for benchmark names, So i was abbreviating some of the names of the benchmark names.

@spiral-ladder

Copy link
Copy Markdown
Member

@spiral-ladder zbench has a character limit for benchmark names, So i was abbreviating some of the names of the benchmark names.

I see! Wasn't aware of the 22-character limit, thanks for letting me know.

@spiral-ladder spiral-ladder left a comment

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.

Looking great - gave a pass through

Comment thread src/config/fork.zig Outdated
Comment on lines +103 to +112
pub fn slotFromStateBytes(state_bytes: []const u8) ?Slot {
if (state_bytes.len < 48) return null;
return std.mem.readInt(u64, state_bytes[40..48], .little);
}

/// Read slot from raw SignedBeaconBlock SSZ bytes (offset 100)
pub fn slotFromBlockBytes(block_bytes: []const u8) ?Slot {
if (block_bytes.len < 108) return null;
return std.mem.readInt(u64, block_bytes[100..108], .little);
}

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.

2 points here:

  1. I think given that these are only utils used in benchmarking we should just keep them in bench/state_transition rather than put them in config/fork.zig. We can move these functions if we ever use them outside of benchmarking.

  2. I think we can assume we're always going to be loading valid bytes here and if not we should be error-ing instead anyway:

Suggested change
pub fn slotFromStateBytes(state_bytes: []const u8) ?Slot {
if (state_bytes.len < 48) return null;
return std.mem.readInt(u64, state_bytes[40..48], .little);
}
/// Read slot from raw SignedBeaconBlock SSZ bytes (offset 100)
pub fn slotFromBlockBytes(block_bytes: []const u8) ?Slot {
if (block_bytes.len < 108) return null;
return std.mem.readInt(u64, block_bytes[100..108], .little);
}
/// Read slot from raw BeaconState SSZ bytes (offset 40)
pub fn slotFromStateBytes(state_bytes: []const u8) Slot {
std.debug.assert(state_bytes.len >= 48);
return std.mem.readInt(u64, state_bytes[40..48], .little);
}
/// Read slot from raw SignedBeaconBlock SSZ bytes (offset 100)
pub fn slotFromBlockBytes(block_bytes: []const u8) Slot {
std.debug.assert(block_bytes.len >= 108);
return std.mem.readInt(u64, block_bytes[100..108], .little);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

moved these and the loadState and loadBlock to a utils file inside bench

Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_epoch.zig
Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment on lines +693 to +694
// Actual processEpoch function
try bench.addParam("process_epoch", &ProcessEpochBench{ .cached_state = cached_state }, .{});

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.

What does "Actual processEpoch function" mean?

@guha-rahul guha-rahul Dec 12, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I meant the non segmented one. Is "continuous" a better name or something more descriptive?

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.

I don't think we need a special name, just non-segmented vs segmented will do.

Comment thread bench/state_transition/utils.zig Outdated
Comment on lines +693 to +694
// Actual processEpoch function
try bench.addParam("process_epoch", &ProcessEpochBench{ .cached_state = cached_state }, .{});

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.

I don't think we need a special name, just non-segmented vs segmented will do.

Comment thread bench/state_transition/utils.zig Outdated
Comment thread bench/state_transition/utils.zig Outdated
Comment thread bench/state_transition/process_block.zig Outdated
@guha-rahul
guha-rahul requested a review from a team as a code owner January 7, 2026 13:22

@spiral-ladder spiral-ladder left a comment

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.

Thanks again for the cleanup! Just a few more loose ends. There's still some things to cleanup but we can leave those for a followup PR.

Comment thread bench/state_transition/process_block.zig Outdated
Comment on lines +87 to +91
const cloned = self.cached_state.clone(allocator) catch unreachable;
defer {
cloned.deinit();
allocator.destroy(cloned);
}

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.

We can save this for a future PR, but we should probably move this clone (and other clones) out of the run definition since cloning should not be part of the cost of benchmarking processXXX

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.

Created #164

Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread zbuild.zon Outdated
Comment thread src/config/fork_seq.zig Outdated

@spiral-ladder spiral-ladder left a comment

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.

last nits

Comment thread bench/state_transition/process_block.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
Comment thread bench/state_transition/process_epoch.zig Outdated
@spiral-ladder
spiral-ladder self-requested a review January 10, 2026 04:44

@spiral-ladder spiral-ladder left a comment

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.

LGTM, thanks for the PR!

@spiral-ladder
spiral-ladder merged commit f7e1f21 into ChainSafe:main Jan 10, 2026
8 checks passed
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.

3 participants