feat: async shuffling refactor - #6521
Conversation
…ling from shufflingCache
…actual class in beacon-node and building mock
| let cachedState: CachedBeaconStateAllForks; | ||
| if (isCachedBeaconState(anchorState) && opts.skipCreateStateCacheIfAvailable) { | ||
| cachedState = anchorState; | ||
| cachedState.epochCtx.shufflingCache.addMetrics(metrics); |
There was a problem hiding this comment.
This is a bit of a funky edge case. I do not like adding the metrics here but there is an instance where the ShufflingCache need to be created before the metrics object (during genesis) and refactoring for that condition was not ideal. So in that circumstance the CachedBeaconState is built with a ShufflingCache and then when the seedState is passed into the chain the metrics are added to the class. This will be extended to also add the Logger when the worker thread for building is added.
| } | ||
| }); | ||
| const expectedShuffling = {epoch: attEpoch} as EpochShuffling; | ||
| const expectedShuffling = {epoch: attEpoch} as unknown as EpochShuffling; |
There was a problem hiding this comment.
epoch is no longer on EpochShuffling but using it here so there is something on the object to assert that its the same object
Performance Report✔️ no performance regression detected 🚀🚀 Significant benchmark improvement detected
Full benchmark results
|
|
this PR is not aligned with the high level design stated in in #6386 where it's recommended to move shuffling from
|
wemeetagain
left a comment
There was a problem hiding this comment.
I committed a few changes which (hopefully) simplify and standardize the shuffling cache interface and implementation.
Still a few questions about how shuffling cache builds will be triggered but this is generally looking good
| **/ | ||
| export const SHUFFLING_CACHE_MAX_EPOCHS = 4; | ||
|
|
||
| export enum ShufflingCacheCaller { |
There was a problem hiding this comment.
If we don't actually need the caller, then we can move the implementation to beacon node like we talked about.
| this.nextActiveIndices = epochTransitionCache.nextEpochShufflingActiveValidatorIndices; | ||
| this.nextShufflingDecisionRoot = getShufflingDecisionBlock(state, this.nextEpoch); | ||
| this.shufflingCache | ||
| .build(state, this.nextEpoch, this.nextShufflingDecisionRoot, this.nextActiveIndices) |
There was a problem hiding this comment.
I thought we wanted to start the build afterwards in beacon-node
| this.previousTargetUnslashedBalanceIncrements = this.currentTargetUnslashedBalanceIncrements; | ||
| this.currentTargetUnslashedBalanceIncrements = 0; | ||
|
|
||
| // Advance time units |
| /** | ||
| * Returns hex string representation of the block root for a given state and epoch | ||
| */ | ||
| export function getShufflingDecisionBlock(state: BeaconStateAllForks, epoch: Epoch): RootHex { |
** NOTE: Note ready for review, but want to trigger CI **
Motivation
Move calculation of next shuffling to async to get it off of critical path during epoch transition. There is a full second during epoch transition used to calculate the
epochCtx.nextShufflingand that can be moved to an async process. Refactored a few pieces of theEpochCacheto make this work and will continue this by creating a worker that moves this calculation to a worker thread. By using a worker thread that is tuned down with NICE we can interleave the long calculation into thread idle time which is ideal. To be continued...Description
beacon-nodetostate-transitionEpochCacheso its available for debugging issues with shuffling builds