seed sync committee state cache (#4592)

By pre-seeding the sync committee cache when applying blocks, we avoid a
significantly expensive validator set traversal / sync committee index
construction during sync / block application - 20-30% sync speedup
post-altair.

* also cache/reload total active balance for another cool 10%
This commit is contained in:
Jacek Sieka 2023-02-07 11:22:22 +01:00 committed by GitHub
parent 171a185e4d
commit 98db005c70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 5 deletions

View File

@ -271,6 +271,8 @@ type
shufflingRef*: ShufflingRef
total_active_balance*: Gwei
# balances, as used in fork choice
effective_balances_bytes*: seq[byte]

View File

@ -534,6 +534,8 @@ func init*(
attester_dependent_root = withState(state):
forkyState.attester_dependent_root
total_active_balance = withState(state):
get_total_active_balance(forkyState.data, cache)
epochRef = EpochRef(
key: dag.epochKey(state.latest_block_id, epoch).expect(
"Valid epoch ancestor when processing state"),
@ -551,7 +553,8 @@ func init*(
# beacon_proposers: Separately filled below
proposer_dependent_root: proposer_dependent_root,
shufflingRef: shufflingRef
shufflingRef: shufflingRef,
total_active_balance: total_active_balance
)
epochStart = epoch.start_slot()
@ -597,12 +600,28 @@ func loadStateCache(
let start_slot = epoch.start_slot()
for i, idx in epochRef[][].beacon_proposers:
cache.beacon_proposer_indices[start_slot + i] = idx
cache.total_active_balance[epoch] = epochRef[][].total_active_balance
load(epoch)
if epoch > 0:
load(epoch - 1)
if dag.head != nil: # nil during init.. sigh
let period = dag.head.slot.sync_committee_period
if period == epoch.sync_committee_period and
period notin cache.sync_committees and
period > dag.cfg.ALTAIR_FORK_EPOCH.sync_committee_period():
# If the block we're aiming for shares ancestry with head, we can reuse
# the cached head committee - this accounts for most "live" cases like
# syncing and checking blocks since the committees rarely change
let periodBsi = dag.atSlot(bid, period.start_slot)
if periodBsi.isSome and periodBsi ==
dag.atSlot(dag.head.bid, period.start_slot):
# We often end up sharing sync committees with head during sync / gossip
# validation / head updates
cache.sync_committees[period] = dag.headSyncCommittees
func containsForkBlock*(dag: ChainDAGRef, root: Eth2Digest): bool =
## Checks for blocks at the finalized checkpoint or newer
KeyedBlockRef.asLookupKey(root) in dag.forkBlocks
@ -1014,6 +1033,10 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
head = shortLog(head), tail = shortLog(dag.tail)
quit 1
withState(dag.headState):
when stateFork >= ConsensusFork.Altair:
dag.headSyncCommittees = forkyState.data.get_sync_committee_cache(cache)
block:
# EpochRef needs an epoch boundary state
assign(dag.epochRefState, dag.headState)
@ -1170,10 +1193,6 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
# have a cache
dag.updateValidatorKeys(getStateField(dag.headState, validators).asSeq())
withState(dag.headState):
when stateFork >= ConsensusFork.Altair:
dag.headSyncCommittees = forkyState.data.get_sync_committee_cache(cache)
info "Block DAG initialized",
head = shortLog(dag.head),
finalizedHead = shortLog(dag.finalizedHead),