Skip to content

Conversation

twoeths
Copy link
Contributor

@twoeths twoeths commented Jul 23, 2025

Motivation

  • lodestar usually has too few attestations in its produced blocks and it has some rooms for improvement.
  • this PR tries to include missing SingleAttestations

Description

  • new SingleAttestation pool
    • store 3 slots up to 32 63 slots depending on total attestation count capped by 100_000 150_000 as analyzed in the PR
    • the goal is to store less than 32 50 MB of SingleAttestations so that it does not affect performance of node
  • new getAttestationsForBlock, for each slot:

TODOs

  • monitor on public networks. Does it affect memory, performance, how many more attestations included?
    • new seen attesters metric tracked
    • new attestation from SingleAttestationPool tracked
    • performance when producing block is even better, performance for aggregated attestation validation is the same
    • memory is really slow but I bound it to 50MB only
  • unit tests
  • left some TODOs for refactor work to make it less diffs to review. Need to work on that in the end ==> will do it in a separate PR
  • test on a real hoodi node to make sure produced blocks are included on chain: block 965135 is proposed by this branch

cc @wemeetagain @nflaig

Copy link
Contributor

github-actions bot commented Jul 23, 2025

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 8a41152 Previous: dea6b94 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 935.50 us/op 1.1811 ms/op 0.79
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 34.174 us/op 39.704 us/op 0.86
BLS verify - blst 726.16 us/op 934.41 us/op 0.78
BLS verifyMultipleSignatures 3 - blst 1.1463 ms/op 1.3190 ms/op 0.87
BLS verifyMultipleSignatures 8 - blst 1.6008 ms/op 1.7590 ms/op 0.91
BLS verifyMultipleSignatures 32 - blst 4.6792 ms/op 5.2655 ms/op 0.89
BLS verifyMultipleSignatures 64 - blst 8.7326 ms/op 9.8942 ms/op 0.88
BLS verifyMultipleSignatures 128 - blst 16.743 ms/op 18.860 ms/op 0.89
BLS deserializing 10000 signatures 670.51 ms/op 750.20 ms/op 0.89
BLS deserializing 100000 signatures 6.6820 s/op 7.4495 s/op 0.90
BLS verifyMultipleSignatures - same message - 3 - blst 856.28 us/op 1.0117 ms/op 0.85
BLS verifyMultipleSignatures - same message - 8 - blst 983.14 us/op 1.1338 ms/op 0.87
BLS verifyMultipleSignatures - same message - 32 - blst 1.6298 ms/op 1.8755 ms/op 0.87
BLS verifyMultipleSignatures - same message - 64 - blst 2.4673 ms/op 2.7611 ms/op 0.89
BLS verifyMultipleSignatures - same message - 128 - blst 4.1678 ms/op 4.7196 ms/op 0.88
BLS aggregatePubkeys 32 - blst 18.667 us/op 20.809 us/op 0.90
BLS aggregatePubkeys 128 - blst 66.633 us/op 74.120 us/op 0.90
notSeenSlots=1 numMissedVotes=1 numBadVotes=10 45.275 ms/op 53.945 ms/op 0.84
notSeenSlots=1 numMissedVotes=0 numBadVotes=4 43.523 ms/op 49.276 ms/op 0.88
notSeenSlots=2 numMissedVotes=1 numBadVotes=10 32.802 ms/op 37.510 ms/op 0.87
getSlashingsAndExits - default max 70.912 us/op 74.812 us/op 0.95
getSlashingsAndExits - 2k 270.40 us/op 296.51 us/op 0.91
proposeBlockBody type=full, size=empty 5.3947 ms/op 5.5826 ms/op 0.97
isKnown best case - 1 super set check 206.00 ns/op 206.00 ns/op 1.00
isKnown normal case - 2 super set checks 203.00 ns/op 205.00 ns/op 0.99
isKnown worse case - 16 super set checks 203.00 ns/op 205.00 ns/op 0.99
InMemoryCheckpointStateCache - add get delete 2.3040 us/op 2.3580 us/op 0.98
validate api signedAggregateAndProof - struct 1.3267 ms/op 1.3735 ms/op 0.97
validate gossip signedAggregateAndProof - struct 1.3258 ms/op 1.4579 ms/op 0.91
batch validate gossip attestation - vc 640000 - chunk 32 111.05 us/op 119.64 us/op 0.93
batch validate gossip attestation - vc 640000 - chunk 64 98.239 us/op 105.73 us/op 0.93
batch validate gossip attestation - vc 640000 - chunk 128 91.192 us/op 97.103 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 256 93.326 us/op 102.72 us/op 0.91
pickEth1Vote - no votes 916.99 us/op 1.0663 ms/op 0.86
pickEth1Vote - max votes 5.0284 ms/op 10.094 ms/op 0.50
pickEth1Vote - Eth1Data hashTreeRoot value x2048 10.332 ms/op 14.892 ms/op 0.69
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 13.980 ms/op 23.655 ms/op 0.59
pickEth1Vote - Eth1Data fastSerialize value x2048 439.31 us/op 438.94 us/op 1.00
pickEth1Vote - Eth1Data fastSerialize tree x2048 2.1563 ms/op 4.9146 ms/op 0.44
bytes32 toHexString 355.00 ns/op 424.00 ns/op 0.84
bytes32 Buffer.toString(hex) 244.00 ns/op 259.00 ns/op 0.94
bytes32 Buffer.toString(hex) from Uint8Array 330.00 ns/op 344.00 ns/op 0.96
bytes32 Buffer.toString(hex) + 0x 243.00 ns/op 260.00 ns/op 0.93
Object access 1 prop 0.11600 ns/op 0.12500 ns/op 0.93
Map access 1 prop 0.11800 ns/op 0.12700 ns/op 0.93
Object get x1000 5.6910 ns/op 5.9810 ns/op 0.95
Map get x1000 6.1670 ns/op 6.4250 ns/op 0.96
Object set x1000 27.479 ns/op 33.967 ns/op 0.81
Map set x1000 18.613 ns/op 22.506 ns/op 0.83
Return object 10000 times 0.27430 ns/op 0.30360 ns/op 0.90
Throw Error 10000 times 4.2233 us/op 4.6670 us/op 0.90
toHex 123.72 ns/op 143.27 ns/op 0.86
Buffer.from 117.58 ns/op 125.36 ns/op 0.94
shared Buffer 76.020 ns/op 80.451 ns/op 0.94
fastMsgIdFn sha256 / 200 bytes 2.1280 us/op 2.3420 us/op 0.91
fastMsgIdFn h32 xxhash / 200 bytes 203.00 ns/op 216.00 ns/op 0.94
fastMsgIdFn h64 xxhash / 200 bytes 265.00 ns/op 275.00 ns/op 0.96
fastMsgIdFn sha256 / 1000 bytes 7.0340 us/op 7.5240 us/op 0.93
fastMsgIdFn h32 xxhash / 1000 bytes 328.00 ns/op 357.00 ns/op 0.92
fastMsgIdFn h64 xxhash / 1000 bytes 337.00 ns/op 355.00 ns/op 0.95
fastMsgIdFn sha256 / 10000 bytes 63.368 us/op 67.653 us/op 0.94
fastMsgIdFn h32 xxhash / 10000 bytes 1.8060 us/op 1.8900 us/op 0.96
fastMsgIdFn h64 xxhash / 10000 bytes 1.2060 us/op 1.2460 us/op 0.97
send data - 1000 256B messages 14.925 ms/op 21.801 ms/op 0.68
send data - 1000 512B messages 18.371 ms/op 24.562 ms/op 0.75
send data - 1000 1024B messages 26.001 ms/op 32.294 ms/op 0.81
send data - 1000 1200B messages 23.347 ms/op 33.900 ms/op 0.69
send data - 1000 2048B messages 24.040 ms/op 40.089 ms/op 0.60
send data - 1000 4096B messages 27.960 ms/op 40.547 ms/op 0.69
send data - 1000 16384B messages 42.973 ms/op 61.918 ms/op 0.69
send data - 1000 65536B messages 110.62 ms/op 148.31 ms/op 0.75
enrSubnets - fastDeserialize 64 bits 898.00 ns/op 962.00 ns/op 0.93
enrSubnets - ssz BitVector 64 bits 335.00 ns/op 351.00 ns/op 0.95
enrSubnets - fastDeserialize 4 bits 135.00 ns/op 140.00 ns/op 0.96
enrSubnets - ssz BitVector 4 bits 332.00 ns/op 356.00 ns/op 0.93
prioritizePeers score -10:0 att 32-0.1 sync 2-0 117.54 us/op 133.97 us/op 0.88
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 140.36 us/op 167.72 us/op 0.84
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 197.35 us/op 244.36 us/op 0.81
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 366.87 us/op 435.08 us/op 0.84
prioritizePeers score 0:0 att 64-1 sync 4-1 446.83 us/op 552.40 us/op 0.81
array of 16000 items push then shift 1.5550 us/op 1.7543 us/op 0.89
LinkedList of 16000 items push then shift 6.7990 ns/op 9.9860 ns/op 0.68
array of 16000 items push then pop 72.639 ns/op 94.488 ns/op 0.77
LinkedList of 16000 items push then pop 6.7250 ns/op 8.8290 ns/op 0.76
array of 24000 items push then shift 2.3198 us/op 2.5631 us/op 0.91
LinkedList of 24000 items push then shift 6.8310 ns/op 9.4140 ns/op 0.73
array of 24000 items push then pop 97.111 ns/op 129.34 ns/op 0.75
LinkedList of 24000 items push then pop 6.7310 ns/op 9.1640 ns/op 0.73
intersect bitArray bitLen 8 6.2610 ns/op 6.9300 ns/op 0.90
intersect array and set length 8 37.145 ns/op 50.464 ns/op 0.74
intersect bitArray bitLen 128 29.178 ns/op 32.303 ns/op 0.90
intersect array and set length 128 609.45 ns/op 726.39 ns/op 0.84
bitArray.getTrueBitIndexes() bitLen 128 991.00 ns/op 1.3050 us/op 0.76
bitArray.getTrueBitIndexes() bitLen 248 1.7420 us/op 2.2570 us/op 0.77
bitArray.getTrueBitIndexes() bitLen 512 3.5720 us/op 4.4810 us/op 0.80
Buffer.concat 32 items 605.00 ns/op 654.00 ns/op 0.93
Uint8Array.set 32 items 916.00 ns/op 2.5170 us/op 0.36
Buffer.copy 1.9800 us/op 3.2550 us/op 0.61
Uint8Array.set - with subarray 1.5210 us/op 3.4050 us/op 0.45
Uint8Array.set - without subarray 872.00 ns/op 2.2820 us/op 0.38
getUint32 - dataview 200.00 ns/op 215.00 ns/op 0.93
getUint32 - manual 122.00 ns/op 133.00 ns/op 0.92
Set add up to 64 items then delete first 2.2174 us/op 2.1531 us/op 1.03
OrderedSet add up to 64 items then delete first 3.3704 us/op 4.5358 us/op 0.74
Set add up to 64 items then delete last 2.4637 us/op 2.4527 us/op 1.00
OrderedSet add up to 64 items then delete last 3.5735 us/op 4.6590 us/op 0.77
Set add up to 64 items then delete middle 2.4460 us/op 2.7449 us/op 0.89
OrderedSet add up to 64 items then delete middle 5.0753 us/op 6.5670 us/op 0.77
Set add up to 128 items then delete first 4.9558 us/op 5.9950 us/op 0.83
OrderedSet add up to 128 items then delete first 7.7378 us/op 8.8666 us/op 0.87
Set add up to 128 items then delete last 4.8568 us/op 6.6276 us/op 0.73
OrderedSet add up to 128 items then delete last 7.1827 us/op 10.708 us/op 0.67
Set add up to 128 items then delete middle 4.7248 us/op 6.5754 us/op 0.72
OrderedSet add up to 128 items then delete middle 13.421 us/op 17.637 us/op 0.76
Set add up to 256 items then delete first 10.281 us/op 13.352 us/op 0.77
OrderedSet add up to 256 items then delete first 15.799 us/op 18.713 us/op 0.84
Set add up to 256 items then delete last 9.6611 us/op 13.221 us/op 0.73
OrderedSet add up to 256 items then delete last 14.529 us/op 20.433 us/op 0.71
Set add up to 256 items then delete middle 9.4966 us/op 13.920 us/op 0.68
OrderedSet add up to 256 items then delete middle 40.777 us/op 47.733 us/op 0.85
transfer serialized Status (84 B) 2.1190 us/op 3.1220 us/op 0.68
copy serialized Status (84 B) 1.0690 us/op 1.8540 us/op 0.58
transfer serialized SignedVoluntaryExit (112 B) 2.1430 us/op 2.9160 us/op 0.73
copy serialized SignedVoluntaryExit (112 B) 1.0960 us/op 1.7210 us/op 0.64
transfer serialized ProposerSlashing (416 B) 2.1630 us/op 3.8750 us/op 0.56
copy serialized ProposerSlashing (416 B) 1.1500 us/op 2.4210 us/op 0.48
transfer serialized Attestation (485 B) 2.1740 us/op 3.3790 us/op 0.64
copy serialized Attestation (485 B) 1.1660 us/op 2.5060 us/op 0.47
transfer serialized AttesterSlashing (33232 B) 2.4150 us/op 3.3590 us/op 0.72
copy serialized AttesterSlashing (33232 B) 3.2550 us/op 6.1050 us/op 0.53
transfer serialized Small SignedBeaconBlock (128000 B) 2.6610 us/op 3.7690 us/op 0.71
copy serialized Small SignedBeaconBlock (128000 B) 8.7510 us/op 16.195 us/op 0.54
transfer serialized Avg SignedBeaconBlock (200000 B) 3.2250 us/op 4.2080 us/op 0.77
copy serialized Avg SignedBeaconBlock (200000 B) 13.537 us/op 24.545 us/op 0.55
transfer serialized BlobsSidecar (524380 B) 3.1960 us/op 4.6470 us/op 0.69
copy serialized BlobsSidecar (524380 B) 89.630 us/op 103.80 us/op 0.86
transfer serialized Big SignedBeaconBlock (1000000 B) 3.3990 us/op 4.8510 us/op 0.70
copy serialized Big SignedBeaconBlock (1000000 B) 168.07 us/op 159.78 us/op 1.05
pass gossip attestations to forkchoice per slot 2.7512 ms/op 2.8994 ms/op 0.95
forkChoice updateHead vc 100000 bc 64 eq 0 455.27 us/op 484.29 us/op 0.94
forkChoice updateHead vc 600000 bc 64 eq 0 2.7914 ms/op 3.1860 ms/op 0.88
forkChoice updateHead vc 1000000 bc 64 eq 0 4.9704 ms/op 5.8463 ms/op 0.85
forkChoice updateHead vc 600000 bc 320 eq 0 2.8059 ms/op 3.4299 ms/op 0.82
forkChoice updateHead vc 600000 bc 1200 eq 0 3.0437 ms/op 3.6308 ms/op 0.84
forkChoice updateHead vc 600000 bc 7200 eq 0 3.0690 ms/op 3.9284 ms/op 0.78
forkChoice updateHead vc 600000 bc 64 eq 1000 10.246 ms/op 11.124 ms/op 0.92
forkChoice updateHead vc 600000 bc 64 eq 10000 10.180 ms/op 11.232 ms/op 0.91
forkChoice updateHead vc 600000 bc 64 eq 300000 13.992 ms/op 16.413 ms/op 0.85
computeDeltas 500000 validators 300 proto nodes 3.7988 ms/op 4.2738 ms/op 0.89
computeDeltas 500000 validators 1200 proto nodes 3.7895 ms/op 4.3230 ms/op 0.88
computeDeltas 500000 validators 7200 proto nodes 3.8095 ms/op 4.2512 ms/op 0.90
computeDeltas 750000 validators 300 proto nodes 5.6459 ms/op 6.3509 ms/op 0.89
computeDeltas 750000 validators 1200 proto nodes 5.6772 ms/op 6.4129 ms/op 0.89
computeDeltas 750000 validators 7200 proto nodes 5.6375 ms/op 6.1393 ms/op 0.92
computeDeltas 1400000 validators 300 proto nodes 10.668 ms/op 11.286 ms/op 0.95
computeDeltas 1400000 validators 1200 proto nodes 10.624 ms/op 11.222 ms/op 0.95
computeDeltas 1400000 validators 7200 proto nodes 10.615 ms/op 11.423 ms/op 0.93
computeDeltas 2100000 validators 300 proto nodes 15.948 ms/op 17.166 ms/op 0.93
computeDeltas 2100000 validators 1200 proto nodes 15.925 ms/op 17.447 ms/op 0.91
computeDeltas 2100000 validators 7200 proto nodes 15.965 ms/op 18.498 ms/op 0.86
altair processAttestation - 250000 vs - 7PWei normalcase 1.9404 ms/op 3.1650 ms/op 0.61
altair processAttestation - 250000 vs - 7PWei worstcase 2.8289 ms/op 4.1796 ms/op 0.68
altair processAttestation - setStatus - 1/6 committees join 120.92 us/op 135.12 us/op 0.89
altair processAttestation - setStatus - 1/3 committees join 232.28 us/op 257.66 us/op 0.90
altair processAttestation - setStatus - 1/2 committees join 326.58 us/op 354.52 us/op 0.92
altair processAttestation - setStatus - 2/3 committees join 424.23 us/op 451.17 us/op 0.94
altair processAttestation - setStatus - 4/5 committees join 587.95 us/op 621.91 us/op 0.95
altair processAttestation - setStatus - 100% committees join 689.50 us/op 727.79 us/op 0.95
altair processBlock - 250000 vs - 7PWei normalcase 4.7698 ms/op 4.8239 ms/op 0.99
altair processBlock - 250000 vs - 7PWei normalcase hashState 30.381 ms/op 30.370 ms/op 1.00
altair processBlock - 250000 vs - 7PWei worstcase 34.656 ms/op 37.938 ms/op 0.91
altair processBlock - 250000 vs - 7PWei worstcase hashState 76.201 ms/op 81.515 ms/op 0.93
phase0 processBlock - 250000 vs - 7PWei normalcase 1.6462 ms/op 1.5719 ms/op 1.05
phase0 processBlock - 250000 vs - 7PWei worstcase 21.028 ms/op 20.774 ms/op 1.01
altair processEth1Data - 250000 vs - 7PWei normalcase 333.42 us/op 373.44 us/op 0.89
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:15 5.7360 us/op 5.8590 us/op 0.98
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:219 34.042 us/op 35.390 us/op 0.96
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:42 9.6790 us/op 11.004 us/op 0.88
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:18 5.9400 us/op 6.5140 us/op 0.91
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1020 133.38 us/op 157.03 us/op 0.85
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11777 1.7441 ms/op 1.9021 ms/op 0.92
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.2309 ms/op 2.4669 ms/op 0.90
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.2355 ms/op 2.4102 ms/op 0.93
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.3603 ms/op 4.8598 ms/op 0.90
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.3178 ms/op 2.4211 ms/op 0.96
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.3744 ms/op 4.7067 ms/op 0.93
Tree 40 250000 create 419.10 ms/op 414.25 ms/op 1.01
Tree 40 250000 get(125000) 139.92 ns/op 146.26 ns/op 0.96
Tree 40 250000 set(125000) 1.4108 us/op 1.4516 us/op 0.97
Tree 40 250000 toArray() 15.082 ms/op 14.906 ms/op 1.01
Tree 40 250000 iterate all - toArray() + loop 14.901 ms/op 18.064 ms/op 0.82
Tree 40 250000 iterate all - get(i) 48.965 ms/op 51.321 ms/op 0.95
Array 250000 create 2.7875 ms/op 2.6384 ms/op 1.06
Array 250000 clone - spread 787.97 us/op 793.43 us/op 0.99
Array 250000 get(125000) 0.40500 ns/op 0.39500 ns/op 1.03
Array 250000 set(125000) 0.43500 ns/op 0.41000 ns/op 1.06
Array 250000 iterate all - loop 81.583 us/op 80.046 us/op 1.02
phase0 afterProcessEpoch - 250000 vs - 7PWei 41.558 ms/op 41.663 ms/op 1.00
Array.fill - length 1000000 3.3233 ms/op 3.3521 ms/op 0.99
Array push - length 1000000 11.809 ms/op 12.030 ms/op 0.98
Array.get 0.25786 ns/op 0.26889 ns/op 0.96
Uint8Array.get 0.42048 ns/op 0.43578 ns/op 0.96
phase0 beforeProcessEpoch - 250000 vs - 7PWei 15.324 ms/op 15.186 ms/op 1.01
altair processEpoch - mainnet_e81889 260.88 ms/op 257.73 ms/op 1.01
mainnet_e81889 - altair beforeProcessEpoch 15.829 ms/op 18.323 ms/op 0.86
mainnet_e81889 - altair processJustificationAndFinalization 5.3350 us/op 5.5370 us/op 0.96
mainnet_e81889 - altair processInactivityUpdates 3.9336 ms/op 4.2354 ms/op 0.93
mainnet_e81889 - altair processRewardsAndPenalties 38.064 ms/op 39.466 ms/op 0.96
mainnet_e81889 - altair processRegistryUpdates 721.00 ns/op 717.00 ns/op 1.01
mainnet_e81889 - altair processSlashings 179.00 ns/op 183.00 ns/op 0.98
mainnet_e81889 - altair processEth1DataReset 169.00 ns/op 178.00 ns/op 0.95
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.1444 ms/op 1.2208 ms/op 0.94
mainnet_e81889 - altair processSlashingsReset 851.00 ns/op 891.00 ns/op 0.96
mainnet_e81889 - altair processRandaoMixesReset 1.1850 us/op 1.1540 us/op 1.03
mainnet_e81889 - altair processHistoricalRootsUpdate 176.00 ns/op 180.00 ns/op 0.98
mainnet_e81889 - altair processParticipationFlagUpdates 509.00 ns/op 513.00 ns/op 0.99
mainnet_e81889 - altair processSyncCommitteeUpdates 137.00 ns/op 142.00 ns/op 0.96
mainnet_e81889 - altair afterProcessEpoch 43.813 ms/op 44.473 ms/op 0.99
capella processEpoch - mainnet_e217614 913.49 ms/op 993.41 ms/op 0.92
mainnet_e217614 - capella beforeProcessEpoch 58.123 ms/op 59.245 ms/op 0.98
mainnet_e217614 - capella processJustificationAndFinalization 5.3020 us/op 5.4480 us/op 0.97
mainnet_e217614 - capella processInactivityUpdates 14.030 ms/op 14.174 ms/op 0.99
mainnet_e217614 - capella processRewardsAndPenalties 182.67 ms/op 168.66 ms/op 1.08
mainnet_e217614 - capella processRegistryUpdates 6.2940 us/op 6.3770 us/op 0.99
mainnet_e217614 - capella processSlashings 181.00 ns/op 182.00 ns/op 0.99
mainnet_e217614 - capella processEth1DataReset 174.00 ns/op 177.00 ns/op 0.98
mainnet_e217614 - capella processEffectiveBalanceUpdates 10.414 ms/op 4.2918 ms/op 2.43
mainnet_e217614 - capella processSlashingsReset 840.00 ns/op 882.00 ns/op 0.95
mainnet_e217614 - capella processRandaoMixesReset 1.1750 us/op 1.1820 us/op 0.99
mainnet_e217614 - capella processHistoricalRootsUpdate 179.00 ns/op 183.00 ns/op 0.98
mainnet_e217614 - capella processParticipationFlagUpdates 510.00 ns/op 528.00 ns/op 0.97
mainnet_e217614 - capella afterProcessEpoch 113.87 ms/op 117.02 ms/op 0.97
phase0 processEpoch - mainnet_e58758 263.63 ms/op 284.14 ms/op 0.93
mainnet_e58758 - phase0 beforeProcessEpoch 68.698 ms/op 75.536 ms/op 0.91
mainnet_e58758 - phase0 processJustificationAndFinalization 5.2760 us/op 5.8020 us/op 0.91
mainnet_e58758 - phase0 processRewardsAndPenalties 34.955 ms/op 34.572 ms/op 1.01
mainnet_e58758 - phase0 processRegistryUpdates 3.1020 us/op 3.1690 us/op 0.98
mainnet_e58758 - phase0 processSlashings 176.00 ns/op 180.00 ns/op 0.98
mainnet_e58758 - phase0 processEth1DataReset 174.00 ns/op 177.00 ns/op 0.98
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1124 ms/op 1.2296 ms/op 0.90
mainnet_e58758 - phase0 processSlashingsReset 873.00 ns/op 934.00 ns/op 0.93
mainnet_e58758 - phase0 processRandaoMixesReset 1.1800 us/op 1.4280 us/op 0.83
mainnet_e58758 - phase0 processHistoricalRootsUpdate 175.00 ns/op 183.00 ns/op 0.96
mainnet_e58758 - phase0 processParticipationRecordUpdates 877.00 ns/op 919.00 ns/op 0.95
mainnet_e58758 - phase0 afterProcessEpoch 34.855 ms/op 36.677 ms/op 0.95
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3087 ms/op 2.3688 ms/op 0.55
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.9352 ms/op 2.4044 ms/op 0.80
altair processInactivityUpdates - 250000 normalcase 15.917 ms/op 18.788 ms/op 0.85
altair processInactivityUpdates - 250000 worstcase 15.949 ms/op 17.528 ms/op 0.91
phase0 processRegistryUpdates - 250000 normalcase 6.5530 us/op 6.9990 us/op 0.94
phase0 processRegistryUpdates - 250000 badcase_full_deposits 214.93 us/op 270.87 us/op 0.79
phase0 processRegistryUpdates - 250000 worstcase 0.5 91.280 ms/op 97.175 ms/op 0.94
altair processRewardsAndPenalties - 250000 normalcase 25.957 ms/op 26.735 ms/op 0.97
altair processRewardsAndPenalties - 250000 worstcase 25.688 ms/op 25.783 ms/op 1.00
phase0 getAttestationDeltas - 250000 normalcase 6.3005 ms/op 8.0280 ms/op 0.78
phase0 getAttestationDeltas - 250000 worstcase 6.6287 ms/op 6.0929 ms/op 1.09
phase0 processSlashings - 250000 worstcase 75.670 us/op 89.289 us/op 0.85
altair processSyncCommitteeUpdates - 250000 10.480 ms/op 10.958 ms/op 0.96
BeaconState.hashTreeRoot - No change 210.00 ns/op 216.00 ns/op 0.97
BeaconState.hashTreeRoot - 1 full validator 89.360 us/op 74.439 us/op 1.20
BeaconState.hashTreeRoot - 32 full validator 967.33 us/op 763.93 us/op 1.27
BeaconState.hashTreeRoot - 512 full validator 9.4339 ms/op 11.348 ms/op 0.83
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 100.80 us/op 96.517 us/op 1.04
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.4041 ms/op 1.6165 ms/op 0.87
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 18.715 ms/op 21.390 ms/op 0.87
BeaconState.hashTreeRoot - 1 balances 68.683 us/op 87.630 us/op 0.78
BeaconState.hashTreeRoot - 32 balances 760.27 us/op 798.83 us/op 0.95
BeaconState.hashTreeRoot - 512 balances 6.9257 ms/op 8.4703 ms/op 0.82
BeaconState.hashTreeRoot - 250000 balances 172.89 ms/op 181.51 ms/op 0.95
aggregationBits - 2048 els - zipIndexesInBitList 20.838 us/op 22.070 us/op 0.94
byteArrayEquals 32 52.479 ns/op 54.067 ns/op 0.97
Buffer.compare 32 16.877 ns/op 19.805 ns/op 0.85
byteArrayEquals 1024 1.5558 us/op 1.6109 us/op 0.97
Buffer.compare 1024 24.032 ns/op 29.552 ns/op 0.81
byteArrayEquals 16384 24.760 us/op 25.634 us/op 0.97
Buffer.compare 16384 175.58 ns/op 199.05 ns/op 0.88
byteArrayEquals 123687377 187.76 ms/op 192.50 ms/op 0.98
Buffer.compare 123687377 6.3469 ms/op 6.5627 ms/op 0.97
byteArrayEquals 32 - diff last byte 51.758 ns/op 52.661 ns/op 0.98
Buffer.compare 32 - diff last byte 16.832 ns/op 17.115 ns/op 0.98
byteArrayEquals 1024 - diff last byte 1.5636 us/op 1.5918 us/op 0.98
Buffer.compare 1024 - diff last byte 25.511 ns/op 25.134 ns/op 1.01
byteArrayEquals 16384 - diff last byte 24.900 us/op 25.331 us/op 0.98
Buffer.compare 16384 - diff last byte 198.29 ns/op 184.71 ns/op 1.07
byteArrayEquals 123687377 - diff last byte 187.11 ms/op 191.66 ms/op 0.98
Buffer.compare 123687377 - diff last byte 6.1105 ms/op 6.5021 ms/op 0.94
byteArrayEquals 32 - random bytes 4.9650 ns/op 4.9940 ns/op 0.99
Buffer.compare 32 - random bytes 16.617 ns/op 17.067 ns/op 0.97
byteArrayEquals 1024 - random bytes 4.9550 ns/op 5.0980 ns/op 0.97
Buffer.compare 1024 - random bytes 16.565 ns/op 17.250 ns/op 0.96
byteArrayEquals 16384 - random bytes 4.9720 ns/op 5.1320 ns/op 0.97
Buffer.compare 16384 - random bytes 16.741 ns/op 17.321 ns/op 0.97
byteArrayEquals 123687377 - random bytes 6.2500 ns/op 6.6300 ns/op 0.94
Buffer.compare 123687377 - random bytes 18.100 ns/op 18.420 ns/op 0.98
regular array get 100000 times 32.290 us/op 36.618 us/op 0.88
wrappedArray get 100000 times 31.517 us/op 33.324 us/op 0.95
arrayWithProxy get 100000 times 12.277 ms/op 14.010 ms/op 0.88
ssz.Root.equals 44.429 ns/op 46.654 ns/op 0.95
byteArrayEquals 43.586 ns/op 45.822 ns/op 0.95
Buffer.compare 9.9860 ns/op 10.393 ns/op 0.96
processSlot - 1 slots 10.149 us/op 10.060 us/op 1.01
processSlot - 32 slots 2.5419 ms/op 1.9589 ms/op 1.30
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 2.8728 ms/op 2.9243 ms/op 0.98
getCommitteeAssignments - req 1 vs - 250000 vc 2.0381 ms/op 2.1272 ms/op 0.96
getCommitteeAssignments - req 100 vs - 250000 vc 3.9555 ms/op 4.1046 ms/op 0.96
getCommitteeAssignments - req 1000 vs - 250000 vc 4.2126 ms/op 4.4973 ms/op 0.94
findModifiedValidators - 10000 modified validators 711.54 ms/op 730.20 ms/op 0.97
findModifiedValidators - 1000 modified validators 636.91 ms/op 700.86 ms/op 0.91
findModifiedValidators - 100 modified validators 230.92 ms/op 274.39 ms/op 0.84
findModifiedValidators - 10 modified validators 152.23 ms/op 222.08 ms/op 0.69
findModifiedValidators - 1 modified validators 150.69 ms/op 167.15 ms/op 0.90
findModifiedValidators - no difference 143.25 ms/op 156.17 ms/op 0.92
compare ViewDUs 5.9682 s/op 6.1525 s/op 0.97
compare each validator Uint8Array 1.4310 s/op 1.5763 s/op 0.91
compare ViewDU to Uint8Array 1.0322 s/op 1.1064 s/op 0.93
migrate state 1000000 validators, 24 modified, 0 new 902.30 ms/op 749.54 ms/op 1.20
migrate state 1000000 validators, 1700 modified, 1000 new 1.1928 s/op 1.1232 s/op 1.06
migrate state 1000000 validators, 3400 modified, 2000 new 1.4241 s/op 1.3103 s/op 1.09
migrate state 1500000 validators, 24 modified, 0 new 904.55 ms/op 891.43 ms/op 1.01
migrate state 1500000 validators, 1700 modified, 1000 new 1.0433 s/op 1.0164 s/op 1.03
migrate state 1500000 validators, 3400 modified, 2000 new 1.2349 s/op 1.3434 s/op 0.92
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.2500 ns/op 4.4500 ns/op 0.96
state getBlockRootAtSlot - 250000 vs - 7PWei 377.89 ns/op 618.99 ns/op 0.61
naive computeProposerIndex 100000 validators 51.079 ms/op 61.052 ms/op 0.84
computeProposerIndex 100000 validators 1.4993 ms/op 1.6360 ms/op 0.92
naiveGetNextSyncCommitteeIndices 1000 validators 7.3575 s/op 8.6133 s/op 0.85
getNextSyncCommitteeIndices 1000 validators 123.69 ms/op 126.65 ms/op 0.98
naiveGetNextSyncCommitteeIndices 10000 validators 7.6660 s/op 8.3521 s/op 0.92
getNextSyncCommitteeIndices 10000 validators 112.07 ms/op 129.54 ms/op 0.87
naiveGetNextSyncCommitteeIndices 100000 validators 7.3742 s/op 8.6709 s/op 0.85
getNextSyncCommitteeIndices 100000 validators 112.01 ms/op 140.37 ms/op 0.80
naive computeShuffledIndex 100000 validators 26.197 s/op 28.035 s/op 0.93
cached computeShuffledIndex 100000 validators 542.04 ms/op 595.32 ms/op 0.91
naive computeShuffledIndex 2000000 validators 459.14 s/op 491.74 s/op 0.93
cached computeShuffledIndex 2000000 validators 38.254 s/op 35.142 s/op 1.09
computeProposers - vc 250000 617.55 us/op 634.60 us/op 0.97
computeEpochShuffling - vc 250000 40.845 ms/op 42.260 ms/op 0.97
getNextSyncCommittee - vc 250000 10.109 ms/op 10.867 ms/op 0.93
computeSigningRoot for AttestationData 20.202 us/op 22.839 us/op 0.88
hash AttestationData serialized data then Buffer.toString(base64) 1.5602 us/op 1.6008 us/op 0.97
toHexString serialized data 1.1672 us/op 1.1990 us/op 0.97
Buffer.toString(base64) 150.20 ns/op 153.55 ns/op 0.98
nodejs block root to RootHex using toHex 136.13 ns/op 141.82 ns/op 0.96
nodejs block root to RootHex using toRootHex 84.377 ns/op 90.634 ns/op 0.93
browser block root to RootHex using the deprecated toHexString 210.22 ns/op 210.27 ns/op 1.00
browser block root to RootHex using toHex 170.55 ns/op 170.42 ns/op 1.00
browser block root to RootHex using toRootHex 159.48 ns/op 159.01 ns/op 1.00

by benchmarkbot/action

Copy link

codecov bot commented Jul 24, 2025

Codecov Report

❌ Patch coverage is 73.21689% with 184 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.93%. Comparing base (48f2ff7) to head (af31d5e).
⚠️ Report is 16 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #8076      +/-   ##
============================================
+ Coverage     55.78%   55.93%   +0.14%     
============================================
  Files           834      836       +2     
  Lines         59535    60444     +909     
  Branches       4602     4721     +119     
============================================
+ Hits          33214    33808     +594     
- Misses        26253    26568     +315     
  Partials         68       68              
🚀 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.

@twoeths
Copy link
Contributor Author

twoeths commented Jul 30, 2025

here are metrics on mainnet node (before and after I subscribe to all subnets)

  • there are more consolidations from the new SingleAttestationPool than the old AggregatedAttestationPool
Screenshot 2025-07-30 at 10 19 24
  • as a result, there are more packed attestations in block, and it's mostly 8 after the node subscribed to all subnets
Screenshot 2025-07-30 at 10 20 29
  • there are a big difference on new seen attesters after the node subscribed to all subnets
Screenshot 2025-07-30 at 10 21 35
  • but time spent for attestation inclusion was actually reduced after the node subscribed to all subnets (because it reached max consolidations quickly, so it does not have to scan all slots)
Screenshot 2025-07-30 at 10 23 38
  • memory of SingleAttestationPool was bound to 50MB, for a regular node it's almost nothing, and for a node subscribing to all subnets it's 1/4 of that capacity (ie it's < 12.5MB)
Screenshot 2025-07-30 at 10 25 53

@twoeths
Copy link
Contributor Author

twoeths commented Jul 30, 2025

will separate this PR into 4 smaller PRs to make it easier to review:

  • enhance AggregatedAttestationPool ==> feat: enhance AggregatedAttestationPool #8095
  • implement SingleAttestationPool
  • main logic to get attestations for block production, populate SingleAttestationPool
  • refactor AggregatedAttestationPool after all: remove unused method, move common types/utils to better places

@ensi321
Copy link
Contributor

ensi321 commented Jul 30, 2025

  • memory of SingleAttestationPool was bound to 50MB, for a regular node it's almost nothing, and for a node subscribing to all subnets it's 1/4 of that capacity (ie it's < 12.5MB)
Screenshot 2025-07-30 at 10 25 53 * there is a minimal impact on aggregated attestation validation, see https://github.com//pull/8084/files#r2240237562

It looks like that 150_000 cap is only enforced during prune and not add. So the 50MB is not a hard bound per se as it can go over 50MB during the slot.
I guess the worst case is there is zero aggregated attestations on the network and all SingleAttestations. For 1 million active validators, there will be 1000000 / 32 = 31250 single attestations, and it will add 31250 * 316 bytes = 9.42MB. So the largest size SingleAttestationPool can reach is 59.42MB, which will be pruned to 50MB onClockSlot. Still nothing significant in my opinion.


/**
* Remove any attestations with a slot lower than `current_slot - MAX_SLOTS_RETAINED`.
* Remove more slots until we have less than `MAX_ATTESTATIONS_RETAINED` attestations in the pool or at least `MIN_SLOTS_RETAINED` slots.
Copy link
Contributor

@ensi321 ensi321 Jul 30, 2025

Choose a reason for hiding this comment

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

Do we still have MIN_SLOTS_RETAINED mechanism? It looks like prune() will prune all attestations except those for current epoch and last epoch, and then prune down to MAX_ATTESTATIONS_RETAINED

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did have MIN_SLOTS_RETAINED then removed since I switched to only store not-seen attestations
I don't see the use of it anymore. In unfinality time it's more important to keep the node stable than trying to find not-seen attestations to include in block

Copy link
Member

Choose a reason for hiding this comment

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

In unfinality time it's more important to keep the node stable than trying to find not-seen attestations to include in block

For single attestations this strategy seems fine as it's not likely they will represent a lot of weight

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.

Include SingleAttestation when producing block

3 participants