Skip to content

perf(types/electra): model Pending* electra types as ContainerNodeStructType#9054

Closed
twoeths wants to merge 2 commits intounstablefrom
te/pending_deposits_container_node_struct
Closed

perf(types/electra): model Pending* electra types as ContainerNodeStructType#9054
twoeths wants to merge 2 commits intounstablefrom
te/pending_deposits_container_node_struct

Conversation

@twoeths
Copy link
Copy Markdown
Contributor

@twoeths twoeths commented Mar 17, 2026

Motivation

processPendingDeposits, processPendingConsolidations, and related epoch functions iterate over up to tens of thousands of PendingDeposit, PendingPartialWithdrawal, and PendingConsolidation items per epoch transition, reading all fields on each item. These were backed by ContainerType, which stores each item as a Merkle tree node — every field read requires tree traversal.

ContainerNodeStructType stores items as plain JS objects (structs), reducing field access to a direct property lookup. A benchmark with 50,000 items and chunked iteration (matching the real access pattern) shows a ~33x speedup: 12.8 ops/s → 417 ops/s.

processPendingDeposits - iterate 50000 deposits, access all fields
    ✔ ContainerType - getReadonlyByRange chunk=100                        12.79264 ops/s    78.16995 ms/op        -         12 runs   1.47 s
    ✔ ContainerNodeStructType - getReadonlyByRange chunk=100              417.2092 ops/s    2.396879 ms/op        -         60 runs  0.968 s

Description

  • Model PendingDeposit, PendingPartialWithdrawal, and PendingConsolidation in packages/types/src/electra/sszTypes.ts as ContainerNodeStructType instead of ContainerType
  • Add benchmark packages/state-transition/test/perf/epoch/processPendingDeposits.test.ts comparing both types with 50,000 items; benchmark is skipped in CI

AI Assistance Disclosure

Used Claude Code to explore the codebase, identify candidates, and write the benchmark.

twoeths and others added 2 commits March 17, 2026 17:13
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 introduces a critical performance enhancement for the Electra epoch transition process. By refactoring the underlying data structure for PendingDeposit, PendingPartialWithdrawal, and PendingConsolidation from a tree-based ContainerType to a plain object ContainerNodeStructType, the PR drastically reduces the overhead of accessing individual fields. This optimization is projected to yield a substantial speedup in operations that iterate over large numbers of these pending items, which is crucial for maintaining efficiency during network events.

Highlights

  • Performance Optimization: Switched PendingDeposit, PendingPartialWithdrawal, and PendingConsolidation types from ContainerType to ContainerNodeStructType in packages/types/src/electra/sszTypes.ts.
  • Significant Speedup: This change results in a ~33x speedup for iterating and accessing fields on these types, as ContainerNodeStructType stores items as plain JS objects, avoiding costly tree traversals.
  • New Benchmark: A new performance benchmark (packages/state-transition/test/perf/epoch/processPendingDeposits.test.ts) was added to validate and demonstrate the performance gains.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/state-transition/test/perf/epoch/processPendingDeposits.test.ts
    • Added a new performance benchmark for PendingDeposit types, comparing ContainerType and ContainerNodeStructType.
  • packages/types/src/electra/sszTypes.ts
    • Imported ContainerNodeStructType.
    • Updated PendingDeposit to use ContainerNodeStructType.
    • Updated PendingPartialWithdrawal to use ContainerNodeStructType.
    • Updated PendingConsolidation to use ContainerNodeStructType.
Activity
  • The author utilized Claude Code for codebase exploration, candidate identification, and benchmark creation.
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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 a significant performance improvement by modeling PendingDeposit, PendingPartialWithdrawal, and PendingConsolidation as ContainerNodeStructType instead of ContainerType. The change is well-motivated, and the included benchmark clearly demonstrates the benefits. The implementation is correct. I have one minor suggestion to improve the readability and structure of the new benchmark test file.

Comment on lines +27 to +37
const view = listType.defaultViewDU();
const defaultDeposit = ssz.electra.PendingDeposit.defaultValue();
for (let i = 0; i < NUM_DEPOSITS; i++) {
if (listType === ListContainer) {
view.push(PendingDepositContainer.toViewDU(defaultDeposit));
} else {
view.push(PendingDepositNodeStruct.toViewDU(defaultDeposit));
}
}
view.commit();
return view;
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

The buildList function can be simplified by hoisting the type check out of the loop. Instead of checking listType on every iteration, you can get the elementType from the list type once before the loop. This makes the code cleaner and slightly more performant.

  const view = listType.defaultViewDU();
  const defaultDeposit = ssz.electra.PendingDeposit.defaultValue();
  const itemType = listType.elementType;
  for (let i = 0; i < NUM_DEPOSITS; i++) {
    view.push(itemType.toViewDU(defaultDeposit));
  }
  view.commit();
  return view;

@github-actions
Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: d02a743 Previous: 8e34c75 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.1958 ms/op 1.0773 ms/op 1.11
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 41.184 us/op 35.548 us/op 1.16
BLS verify - blst 942.48 us/op 1.0497 ms/op 0.90
BLS verifyMultipleSignatures 3 - blst 1.4220 ms/op 1.2778 ms/op 1.11
BLS verifyMultipleSignatures 8 - blst 1.9764 ms/op 1.9308 ms/op 1.02
BLS verifyMultipleSignatures 32 - blst 5.9389 ms/op 5.7119 ms/op 1.04
BLS verifyMultipleSignatures 64 - blst 11.235 ms/op 11.027 ms/op 1.02
BLS verifyMultipleSignatures 128 - blst 18.481 ms/op 17.848 ms/op 1.04
BLS deserializing 10000 signatures 734.43 ms/op 701.13 ms/op 1.05
BLS deserializing 100000 signatures 7.2472 s/op 6.9871 s/op 1.04
BLS verifyMultipleSignatures - same message - 3 - blst 1.0412 ms/op 880.11 us/op 1.18
BLS verifyMultipleSignatures - same message - 8 - blst 1.1995 ms/op 992.07 us/op 1.21
BLS verifyMultipleSignatures - same message - 32 - blst 1.7909 ms/op 1.7066 ms/op 1.05
BLS verifyMultipleSignatures - same message - 64 - blst 2.7545 ms/op 2.5819 ms/op 1.07
BLS verifyMultipleSignatures - same message - 128 - blst 4.5862 ms/op 4.4333 ms/op 1.03
BLS aggregatePubkeys 32 - blst 19.990 us/op 19.584 us/op 1.02
BLS aggregatePubkeys 128 - blst 71.395 us/op 70.664 us/op 1.01
getSlashingsAndExits - default max 74.090 us/op 79.058 us/op 0.94
getSlashingsAndExits - 2k 362.58 us/op 350.40 us/op 1.03
isKnown best case - 1 super set check 240.00 ns/op 209.00 ns/op 1.15
isKnown normal case - 2 super set checks 207.00 ns/op 202.00 ns/op 1.02
isKnown worse case - 16 super set checks 207.00 ns/op 208.00 ns/op 1.00
validate api signedAggregateAndProof - struct 2.2200 ms/op 1.5852 ms/op 1.40
validate gossip signedAggregateAndProof - struct 2.0258 ms/op 1.8312 ms/op 1.11
batch validate gossip attestation - vc 640000 - chunk 32 127.28 us/op 147.20 us/op 0.86
batch validate gossip attestation - vc 640000 - chunk 64 114.42 us/op 121.35 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 128 106.63 us/op 110.54 us/op 0.96
batch validate gossip attestation - vc 640000 - chunk 256 112.86 us/op 102.76 us/op 1.10
bytes32 toHexString 369.00 ns/op 400.00 ns/op 0.92
bytes32 Buffer.toString(hex) 253.00 ns/op 298.00 ns/op 0.85
bytes32 Buffer.toString(hex) from Uint8Array 318.00 ns/op 430.00 ns/op 0.74
bytes32 Buffer.toString(hex) + 0x 254.00 ns/op 253.00 ns/op 1.00
Return object 10000 times 0.24230 ns/op 0.24820 ns/op 0.98
Throw Error 10000 times 4.4508 us/op 4.5167 us/op 0.99
toHex 150.63 ns/op 147.72 ns/op 1.02
Buffer.from 132.88 ns/op 142.41 ns/op 0.93
shared Buffer 80.721 ns/op 81.153 ns/op 0.99
fastMsgIdFn sha256 / 200 bytes 2.1400 us/op 2.0180 us/op 1.06
fastMsgIdFn h32 xxhash / 200 bytes 246.00 ns/op 237.00 ns/op 1.04
fastMsgIdFn h64 xxhash / 200 bytes 271.00 ns/op 357.00 ns/op 0.76
fastMsgIdFn sha256 / 1000 bytes 6.2760 us/op 6.3070 us/op 1.00
fastMsgIdFn h32 xxhash / 1000 bytes 320.00 ns/op 307.00 ns/op 1.04
fastMsgIdFn h64 xxhash / 1000 bytes 381.00 ns/op 339.00 ns/op 1.12
fastMsgIdFn sha256 / 10000 bytes 54.389 us/op 55.785 us/op 0.97
fastMsgIdFn h32 xxhash / 10000 bytes 1.4170 us/op 1.4370 us/op 0.99
fastMsgIdFn h64 xxhash / 10000 bytes 1.0540 us/op 951.00 ns/op 1.11
send data - 1000 256B messages 5.7190 ms/op 5.6132 ms/op 1.02
send data - 1000 512B messages 4.7559 ms/op 4.7238 ms/op 1.01
send data - 1000 1024B messages 5.1363 ms/op 5.2017 ms/op 0.99
send data - 1000 1200B messages 6.2778 ms/op 6.3770 ms/op 0.98
send data - 1000 2048B messages 6.4271 ms/op 5.5635 ms/op 1.16
send data - 1000 4096B messages 8.5976 ms/op 7.6628 ms/op 1.12
send data - 1000 16384B messages 43.124 ms/op 42.260 ms/op 1.02
send data - 1000 65536B messages 103.66 ms/op 184.51 ms/op 0.56
enrSubnets - fastDeserialize 64 bits 909.00 ns/op 896.00 ns/op 1.01
enrSubnets - ssz BitVector 64 bits 410.00 ns/op 380.00 ns/op 1.08
enrSubnets - fastDeserialize 4 bits 147.00 ns/op 155.00 ns/op 0.95
enrSubnets - ssz BitVector 4 bits 394.00 ns/op 388.00 ns/op 1.02
prioritizePeers score -10:0 att 32-0.1 sync 2-0 298.03 us/op 236.81 us/op 1.26
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 292.51 us/op 255.76 us/op 1.14
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 418.81 us/op 387.89 us/op 1.08
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 787.28 us/op 706.07 us/op 1.12
prioritizePeers score 0:0 att 64-1 sync 4-1 941.54 us/op 852.73 us/op 1.10
array of 16000 items push then shift 1.7108 us/op 1.6845 us/op 1.02
LinkedList of 16000 items push then shift 8.2650 ns/op 7.8090 ns/op 1.06
array of 16000 items push then pop 80.058 ns/op 86.829 ns/op 0.92
LinkedList of 16000 items push then pop 7.5370 ns/op 7.5110 ns/op 1.00
array of 24000 items push then shift 2.5008 us/op 2.4159 us/op 1.04
LinkedList of 24000 items push then shift 8.1770 ns/op 7.4440 ns/op 1.10
array of 24000 items push then pop 114.52 ns/op 107.41 ns/op 1.07
LinkedList of 24000 items push then pop 7.8680 ns/op 7.2650 ns/op 1.08
intersect bitArray bitLen 8 6.1300 ns/op 5.8580 ns/op 1.05
intersect array and set length 8 35.317 ns/op 34.185 ns/op 1.03
intersect bitArray bitLen 128 30.123 ns/op 29.203 ns/op 1.03
intersect array and set length 128 587.54 ns/op 550.43 ns/op 1.07
bitArray.getTrueBitIndexes() bitLen 128 1.1330 us/op 1.0400 us/op 1.09
bitArray.getTrueBitIndexes() bitLen 248 1.9530 us/op 1.9290 us/op 1.01
bitArray.getTrueBitIndexes() bitLen 512 5.2340 us/op 4.0250 us/op 1.30
Full columns - reconstruct all 6 blobs 211.87 us/op 252.95 us/op 0.84
Full columns - reconstruct half of the blobs out of 6 97.257 us/op 106.52 us/op 0.91
Full columns - reconstruct single blob out of 6 32.703 us/op 54.354 us/op 0.60
Half columns - reconstruct all 6 blobs 288.51 ms/op 273.22 ms/op 1.06
Half columns - reconstruct half of the blobs out of 6 147.90 ms/op 140.09 ms/op 1.06
Half columns - reconstruct single blob out of 6 54.054 ms/op 52.451 ms/op 1.03
Full columns - reconstruct all 10 blobs 333.42 us/op 313.42 us/op 1.06
Full columns - reconstruct half of the blobs out of 10 195.88 us/op 180.37 us/op 1.09
Full columns - reconstruct single blob out of 10 34.523 us/op 32.104 us/op 1.08
Half columns - reconstruct all 10 blobs 486.87 ms/op 443.57 ms/op 1.10
Half columns - reconstruct half of the blobs out of 10 246.67 ms/op 224.43 ms/op 1.10
Half columns - reconstruct single blob out of 10 55.933 ms/op 49.190 ms/op 1.14
Full columns - reconstruct all 20 blobs 578.55 us/op 617.48 us/op 0.94
Full columns - reconstruct half of the blobs out of 20 312.92 us/op 306.11 us/op 1.02
Full columns - reconstruct single blob out of 20 32.582 us/op 31.055 us/op 1.05
Half columns - reconstruct all 20 blobs 924.77 ms/op 874.94 ms/op 1.06
Half columns - reconstruct half of the blobs out of 20 475.85 ms/op 449.59 ms/op 1.06
Half columns - reconstruct single blob out of 20 52.631 ms/op 51.796 ms/op 1.02
Set add up to 64 items then delete first 2.5910 us/op 2.0453 us/op 1.27
OrderedSet add up to 64 items then delete first 3.6632 us/op 3.0554 us/op 1.20
Set add up to 64 items then delete last 2.7007 us/op 2.3080 us/op 1.17
OrderedSet add up to 64 items then delete last 4.0831 us/op 3.4644 us/op 1.18
Set add up to 64 items then delete middle 2.7253 us/op 2.3123 us/op 1.18
OrderedSet add up to 64 items then delete middle 5.6105 us/op 5.0210 us/op 1.12
Set add up to 128 items then delete first 5.1934 us/op 4.6733 us/op 1.11
OrderedSet add up to 128 items then delete first 7.6142 us/op 6.6835 us/op 1.14
Set add up to 128 items then delete last 5.7400 us/op 4.6483 us/op 1.23
OrderedSet add up to 128 items then delete last 7.7556 us/op 6.8894 us/op 1.13
Set add up to 128 items then delete middle 5.1262 us/op 4.5549 us/op 1.13
OrderedSet add up to 128 items then delete middle 15.432 us/op 13.252 us/op 1.16
Set add up to 256 items then delete first 10.980 us/op 10.089 us/op 1.09
OrderedSet add up to 256 items then delete first 17.894 us/op 14.179 us/op 1.26
Set add up to 256 items then delete last 10.470 us/op 9.5974 us/op 1.09
OrderedSet add up to 256 items then delete last 15.991 us/op 14.263 us/op 1.12
Set add up to 256 items then delete middle 10.305 us/op 9.4282 us/op 1.09
OrderedSet add up to 256 items then delete middle 46.307 us/op 40.468 us/op 1.14
pass gossip attestations to forkchoice per slot 507.40 us/op 487.83 us/op 1.04
computeDeltas 1400000 validators 0% inactive 14.741 ms/op 14.193 ms/op 1.04
computeDeltas 1400000 validators 10% inactive 13.737 ms/op 13.287 ms/op 1.03
computeDeltas 1400000 validators 20% inactive 12.883 ms/op 12.612 ms/op 1.02
computeDeltas 1400000 validators 50% inactive 9.9658 ms/op 9.5239 ms/op 1.05
computeDeltas 2100000 validators 0% inactive 22.051 ms/op 21.233 ms/op 1.04
computeDeltas 2100000 validators 10% inactive 21.701 ms/op 19.888 ms/op 1.09
computeDeltas 2100000 validators 20% inactive 19.954 ms/op 18.547 ms/op 1.08
computeDeltas 2100000 validators 50% inactive 15.425 ms/op 14.429 ms/op 1.07
altair processAttestation - setStatus - 1/6 committees join 661.00 ns/op 549.00 ns/op 1.20
altair processAttestation - setStatus - 1/3 committees join 943.00 ns/op 1.0960 us/op 0.86
altair processAttestation - setStatus - 1/2 committees join 1.3540 us/op 1.2640 us/op 1.07
altair processAttestation - setStatus - 2/3 committees join 1.5200 us/op 1.4440 us/op 1.05
altair processAttestation - setStatus - 4/5 committees join 1.7570 us/op 1.6770 us/op 1.05
altair processAttestation - setStatus - 100% committees join 2.3070 us/op 1.9610 us/op 1.18
phase0 processBlock - 250000 vs - 7PWei normalcase 1.8139 ms/op 1.7888 ms/op 1.01
phase0 processBlock - 250000 vs - 7PWei worstcase 20.808 ms/op 24.477 ms/op 0.85
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 6.4220 us/op 8.8180 us/op 0.73
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 42.161 us/op 32.651 us/op 1.29
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 11.589 us/op 11.126 us/op 1.04
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 7.3660 us/op 6.4760 us/op 1.14
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 187.22 us/op 204.22 us/op 0.92
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.9365 ms/op 1.7666 ms/op 1.10
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.3980 ms/op 2.9564 ms/op 0.81
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.8237 ms/op 2.2799 ms/op 1.24
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 5.1644 ms/op 4.5159 ms/op 1.14
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 3.1104 ms/op 2.7105 ms/op 1.15
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 5.0135 ms/op 4.7885 ms/op 1.05
Tree 40 250000 create 386.62 ms/op 378.91 ms/op 1.02
Tree 40 250000 get(125000) 136.40 ns/op 133.53 ns/op 1.02
Tree 40 250000 set(125000) 1.2382 us/op 1.2748 us/op 0.97
Tree 40 250000 toArray() 15.174 ms/op 14.960 ms/op 1.01
Tree 40 250000 iterate all - toArray() + loop 14.271 ms/op 16.664 ms/op 0.86
Tree 40 250000 iterate all - get(i) 46.640 ms/op 46.368 ms/op 1.01
Array 250000 create 2.5212 ms/op 2.5283 ms/op 1.00
Array 250000 clone - spread 840.95 us/op 827.24 us/op 1.02
Array 250000 get(125000) 0.39700 ns/op 0.39300 ns/op 1.01
Array 250000 set(125000) 0.40000 ns/op 0.35500 ns/op 1.13
Array 250000 iterate all - loop 63.788 us/op 62.832 us/op 1.02
phase0 afterProcessEpoch - 250000 vs - 7PWei 42.337 ms/op 41.994 ms/op 1.01
Array.fill - length 1000000 3.0167 ms/op 2.8356 ms/op 1.06
Array push - length 1000000 9.7250 ms/op 10.591 ms/op 0.92
Array.get 0.22374 ns/op 0.21524 ns/op 1.04
Uint8Array.get 0.22781 ns/op 0.21943 ns/op 1.04
phase0 beforeProcessEpoch - 250000 vs - 7PWei 19.192 ms/op 15.964 ms/op 1.20
altair processEpoch - mainnet_e81889 343.75 ms/op 306.99 ms/op 1.12
mainnet_e81889 - altair beforeProcessEpoch 35.352 ms/op 16.138 ms/op 2.19
mainnet_e81889 - altair processJustificationAndFinalization 9.2400 us/op 8.7290 us/op 1.06
mainnet_e81889 - altair processInactivityUpdates 5.8042 ms/op 3.8608 ms/op 1.50
mainnet_e81889 - altair processRewardsAndPenalties 22.594 ms/op 19.986 ms/op 1.13
mainnet_e81889 - altair processRegistryUpdates 695.00 ns/op 856.00 ns/op 0.81
mainnet_e81889 - altair processSlashings 184.00 ns/op 212.00 ns/op 0.87
mainnet_e81889 - altair processEth1DataReset 185.00 ns/op 162.00 ns/op 1.14
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.9258 ms/op 1.2980 ms/op 1.48
mainnet_e81889 - altair processSlashingsReset 951.00 ns/op 855.00 ns/op 1.11
mainnet_e81889 - altair processRandaoMixesReset 1.6010 us/op 1.8110 us/op 0.88
mainnet_e81889 - altair processHistoricalRootsUpdate 200.00 ns/op 216.00 ns/op 0.93
mainnet_e81889 - altair processParticipationFlagUpdates 567.00 ns/op 504.00 ns/op 1.13
mainnet_e81889 - altair processSyncCommitteeUpdates 157.00 ns/op 140.00 ns/op 1.12
mainnet_e81889 - altair afterProcessEpoch 44.557 ms/op 42.699 ms/op 1.04
capella processEpoch - mainnet_e217614 870.82 ms/op
mainnet_e217614 - capella beforeProcessEpoch 62.753 ms/op
mainnet_e217614 - capella processJustificationAndFinalization 6.9920 us/op
mainnet_e217614 - capella processInactivityUpdates 20.123 ms/op
mainnet_e217614 - capella processRewardsAndPenalties 113.20 ms/op
mainnet_e217614 - capella processRegistryUpdates 6.4020 us/op
mainnet_e217614 - capella processSlashings 194.00 ns/op
mainnet_e217614 - capella processEth1DataReset 182.00 ns/op
mainnet_e217614 - capella processEffectiveBalanceUpdates 15.454 ms/op
mainnet_e217614 - capella processSlashingsReset 924.00 ns/op
mainnet_e217614 - capella processRandaoMixesReset 1.2220 us/op
mainnet_e217614 - capella processHistoricalRootsUpdate 198.00 ns/op
mainnet_e217614 - capella processParticipationFlagUpdates 557.00 ns/op
mainnet_e217614 - capella afterProcessEpoch 117.91 ms/op
phase0 processEpoch - mainnet_e58758 326.07 ms/op 248.96 ms/op 1.31
mainnet_e58758 - phase0 beforeProcessEpoch 67.750 ms/op 51.478 ms/op 1.32
mainnet_e58758 - phase0 processJustificationAndFinalization 6.3390 us/op 7.5670 us/op 0.84
mainnet_e58758 - phase0 processRewardsAndPenalties 19.035 ms/op 20.814 ms/op 0.91
mainnet_e58758 - phase0 processRegistryUpdates 3.0740 us/op 2.7670 us/op 1.11
mainnet_e58758 - phase0 processSlashings 195.00 ns/op 180.00 ns/op 1.08
mainnet_e58758 - phase0 processEth1DataReset 196.00 ns/op 208.00 ns/op 0.94
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1679 ms/op 1.3270 ms/op 0.88
mainnet_e58758 - phase0 processSlashingsReset 1.5260 us/op 920.00 ns/op 1.66
mainnet_e58758 - phase0 processRandaoMixesReset 1.3900 us/op 1.1240 us/op 1.24
mainnet_e58758 - phase0 processHistoricalRootsUpdate 168.00 ns/op 177.00 ns/op 0.95
mainnet_e58758 - phase0 processParticipationRecordUpdates 972.00 ns/op 922.00 ns/op 1.05
mainnet_e58758 - phase0 afterProcessEpoch 35.855 ms/op 34.446 ms/op 1.04
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.9260 ms/op 1.6303 ms/op 1.18
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 2.1967 ms/op 1.9398 ms/op 1.13
altair processInactivityUpdates - 250000 normalcase 70.715 us/op 66.690 us/op 1.06
altair processInactivityUpdates - 250000 worstcase 74.152 us/op 67.170 us/op 1.10
phase0 processRegistryUpdates - 250000 normalcase 4.9340 us/op 7.6970 us/op 0.64
phase0 processRegistryUpdates - 250000 badcase_full_deposits 289.69 us/op 370.63 us/op 0.78
phase0 processRegistryUpdates - 250000 worstcase 0.5 79.996 ms/op 75.470 ms/op 1.06
altair processRewardsAndPenalties - 250000 normalcase 121.77 us/op 129.47 us/op 0.94
altair processRewardsAndPenalties - 250000 worstcase 118.73 us/op 128.68 us/op 0.92
phase0 getAttestationDeltas - 250000 normalcase 7.3348 ms/op 6.5750 ms/op 1.12
phase0 getAttestationDeltas - 250000 worstcase 7.2097 ms/op 6.6494 ms/op 1.08
phase0 processSlashings - 250000 worstcase 113.24 us/op 119.98 us/op 0.94
altair processSyncCommitteeUpdates - 250000 9.0248 ms/op 7.9337 ms/op 1.14
BeaconState.hashTreeRoot - No change 235.00 ns/op 193.00 ns/op 1.22
BeaconState.hashTreeRoot - 1 full validator 96.803 us/op 86.503 us/op 1.12
BeaconState.hashTreeRoot - 32 full validator 1.0709 ms/op 1.0063 ms/op 1.06
BeaconState.hashTreeRoot - 512 full validator 13.069 ms/op 8.4375 ms/op 1.55
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 112.22 us/op 108.88 us/op 1.03
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.7649 ms/op 1.7321 ms/op 1.02
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 17.967 ms/op 15.958 ms/op 1.13
BeaconState.hashTreeRoot - 1 balances 89.278 us/op 78.019 us/op 1.14
BeaconState.hashTreeRoot - 32 balances 963.36 us/op 798.09 us/op 1.21
BeaconState.hashTreeRoot - 512 balances 7.2380 ms/op 6.3128 ms/op 1.15
BeaconState.hashTreeRoot - 250000 balances 148.44 ms/op 199.58 ms/op 0.74
aggregationBits - 2048 els - zipIndexesInBitList 21.610 us/op 20.824 us/op 1.04
regular array get 100000 times 35.982 us/op 24.681 us/op 1.46
wrappedArray get 100000 times 25.578 us/op 24.608 us/op 1.04
arrayWithProxy get 100000 times 15.783 ms/op 14.341 ms/op 1.10
ssz.Root.equals 54.421 ns/op 23.803 ns/op 2.29
byteArrayEquals 24.803 ns/op 23.438 ns/op 1.06
Buffer.compare 10.154 ns/op 9.9680 ns/op 1.02
processSlot - 1 slots 12.255 us/op 11.405 us/op 1.07
processSlot - 32 slots 2.6067 ms/op 2.2273 ms/op 1.17
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 5.5695 ms/op 3.8448 ms/op 1.45
getCommitteeAssignments - req 1 vs - 250000 vc 1.9178 ms/op 1.7592 ms/op 1.09
getCommitteeAssignments - req 100 vs - 250000 vc 3.7599 ms/op 3.4794 ms/op 1.08
getCommitteeAssignments - req 1000 vs - 250000 vc 4.1940 ms/op 3.7213 ms/op 1.13
findModifiedValidators - 10000 modified validators 460.20 ms/op 653.65 ms/op 0.70
findModifiedValidators - 1000 modified validators 459.52 ms/op 440.68 ms/op 1.04
findModifiedValidators - 100 modified validators 198.03 ms/op 266.73 ms/op 0.74
findModifiedValidators - 10 modified validators 179.91 ms/op 229.85 ms/op 0.78
findModifiedValidators - 1 modified validators 262.69 ms/op 182.99 ms/op 1.44
findModifiedValidators - no difference 221.72 ms/op 165.25 ms/op 1.34
migrate state 1500000 validators, 3400 modified, 2000 new 391.02 ms/op 385.11 ms/op 1.02
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.9700 ns/op 4.0900 ns/op 1.22
state getBlockRootAtSlot - 250000 vs - 7PWei 503.03 ns/op 598.28 ns/op 0.84
computeProposerIndex 100000 validators 1.5922 ms/op 1.5281 ms/op 1.04
getNextSyncCommitteeIndices 1000 validators 3.4967 ms/op 3.2959 ms/op 1.06
getNextSyncCommitteeIndices 10000 validators 3.5025 ms/op 3.3122 ms/op 1.06
getNextSyncCommitteeIndices 100000 validators 3.5574 ms/op 3.3223 ms/op 1.07
computeProposers - vc 250000 645.46 us/op 613.81 us/op 1.05
computeEpochShuffling - vc 250000 43.660 ms/op 41.181 ms/op 1.06
getNextSyncCommittee - vc 250000 11.324 ms/op 10.341 ms/op 1.10
nodejs block root to RootHex using toHex 157.10 ns/op 142.69 ns/op 1.10
nodejs block root to RootHex using toRootHex 90.029 ns/op 89.261 ns/op 1.01
nodejs fromHex(blob) 205.00 us/op 482.95 us/op 0.42
nodejs fromHexInto(blob) 728.39 us/op 673.67 us/op 1.08
nodejs block root to RootHex using the deprecated toHexString 451.23 ns/op 356.10 ns/op 1.27
nodejs byteArrayEquals 32 bytes (block root) 29.673 ns/op 27.391 ns/op 1.08
nodejs byteArrayEquals 48 bytes (pubkey) 42.353 ns/op 39.153 ns/op 1.08
nodejs byteArrayEquals 96 bytes (signature) 42.033 ns/op 40.015 ns/op 1.05
nodejs byteArrayEquals 1024 bytes 48.495 ns/op 44.321 ns/op 1.09
nodejs byteArrayEquals 131072 bytes (blob) 1.9442 us/op 1.8011 us/op 1.08
browser block root to RootHex using toHex 170.33 ns/op 155.90 ns/op 1.09
browser block root to RootHex using toRootHex 160.09 ns/op 147.79 ns/op 1.08
browser fromHex(blob) 1.1873 ms/op 1.0998 ms/op 1.08
browser fromHexInto(blob) 700.14 us/op 692.56 us/op 1.01
browser block root to RootHex using the deprecated toHexString 397.30 ns/op 339.06 ns/op 1.17
browser byteArrayEquals 32 bytes (block root) 31.425 ns/op 30.853 ns/op 1.02
browser byteArrayEquals 48 bytes (pubkey) 43.836 ns/op 43.052 ns/op 1.02
browser byteArrayEquals 96 bytes (signature) 85.814 ns/op 84.432 ns/op 1.02
browser byteArrayEquals 1024 bytes 807.31 ns/op 790.92 ns/op 1.02
browser byteArrayEquals 131072 bytes (blob) 102.26 us/op 100.14 us/op 1.02

by benchmarkbot/action

@twoeths
Copy link
Copy Markdown
Contributor Author

twoeths commented Mar 18, 2026

the result is under my expectation, it's clear that gc is the main factor for each of the spike

Screenshot 2026-03-18 at 09 09 09

I'd bring this idea to lodestar-z instead ChainSafe/lodestar-z#232

@twoeths twoeths closed this Mar 18, 2026
@twoeths twoeths deleted the te/pending_deposits_container_node_struct branch March 18, 2026 02:15
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.32%. Comparing base (8e34c75) to head (57a81f5).
⚠️ Report is 2 commits behind head on unstable.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #9054   +/-   ##
=========================================
  Coverage     52.32%   52.32%           
=========================================
  Files           848      848           
  Lines         62326    62326           
  Branches       4572     4572           
=========================================
  Hits          32612    32612           
  Misses        29649    29649           
  Partials         65       65           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant