From 8e09d4cdeab319264f0f5cfc6d6cbee6f11e1e5d Mon Sep 17 00:00:00 2001 From: Tuyen Nguyen Date: Wed, 24 Jul 2024 15:56:57 +0700 Subject: [PATCH] feat: apply ReusableListIterator --- .../src/block/processEth1Data.ts | 14 ++++++------ .../src/cache/epochTransitionCache.ts | 22 +++++++++++-------- packages/state-transition/src/util/balance.ts | 14 +++++++----- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/state-transition/src/block/processEth1Data.ts b/packages/state-transition/src/block/processEth1Data.ts index 2b6d8b62d79c..fe40f026028a 100644 --- a/packages/state-transition/src/block/processEth1Data.ts +++ b/packages/state-transition/src/block/processEth1Data.ts @@ -1,5 +1,5 @@ import {Node} from "@chainsafe/persistent-merkle-tree"; -import {CompositeViewDU} from "@chainsafe/ssz"; +import {CompositeViewDU, ReusableListIterator} from "@chainsafe/ssz"; import {EPOCHS_PER_ETH1_VOTING_PERIOD, SLOTS_PER_EPOCH} from "@lodestar/params"; import {phase0, ssz} from "@lodestar/types"; import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js"; @@ -26,7 +26,7 @@ export function processEth1Data(state: CachedBeaconStateAllForks, eth1Data: phas /** * This data is reused and never gc. */ -const eth1DataVotes = new Array>(); +const eth1DataVotes = new ReusableListIterator>(); /** * Returns true if adding the given `eth1Data` to `state.eth1DataVotes` would * result in a change to `state.eth1Data`. @@ -52,11 +52,11 @@ export function becomesNewEth1Data( // Then isEqualEth1DataView compares cached roots (HashObject as of Jan 2022) which is much cheaper // than doing structural equality, which requires tree -> value conversions let sameVotesCount = 0; - // const eth1DataVotes = state.eth1DataVotes.getAllReadonly(); - eth1DataVotes.length = state.eth1DataVotes.length; - state.eth1DataVotes.getAllReadonly(eth1DataVotes); - for (let i = 0; i < eth1DataVotes.length; i++) { - if (isEqualEth1DataView(eth1DataVotes[i], newEth1Data)) { + eth1DataVotes.reset(); + state.eth1DataVotes.getAllReadonlyIter(eth1DataVotes); + eth1DataVotes.clean(); + for (const eth1DataVote of eth1DataVotes) { + if (isEqualEth1DataView(eth1DataVote, newEth1Data)) { sameVotesCount++; } } diff --git a/packages/state-transition/src/cache/epochTransitionCache.ts b/packages/state-transition/src/cache/epochTransitionCache.ts index 705966c58a67..0c0a908ecc15 100644 --- a/packages/state-transition/src/cache/epochTransitionCache.ts +++ b/packages/state-transition/src/cache/epochTransitionCache.ts @@ -16,6 +16,7 @@ import { import {CachedBeaconStateAllForks, CachedBeaconStateAltair, CachedBeaconStatePhase0} from "../index.js"; import {computeBaseRewardPerIncrement} from "../util/altair.js"; import {processPendingAttestations} from "../epoch/processPendingAttestations.js"; +import {ReusableListIterator} from "@chainsafe/ssz"; export type EpochTransitionCacheOpts = { /** @@ -200,7 +201,7 @@ const flags = new Array(); /** * This data is reused and never gc. */ -const validators = new Array(); +const validators = new ReusableListIterator(); const previousEpochParticipation = new Array(); const currentEpochParticipation = new Array(); @@ -221,7 +222,7 @@ export function beforeProcessEpoch( const eligibleValidatorIndices: ValidatorIndex[] = []; const indicesToSlash: ValidatorIndex[] = []; const indicesEligibleForActivationQueue: ValidatorIndex[] = []; - const indicesEligibleForActivation: ValidatorIndex[] = []; + const indicesEligibleForActivation: {validatorIndex: ValidatorIndex; activationEligibilityEpoch: Epoch}[] = []; const indicesToEject: ValidatorIndex[] = []; const nextEpochShufflingActiveValidatorIndices: ValidatorIndex[] = []; @@ -230,8 +231,9 @@ export function beforeProcessEpoch( // To optimize memory each validator node in `state.validators` is represented with a special node type // `BranchNodeStruct` that represents the data as struct internally. This utility grabs the struct data directly // from the nodes without any extra transformation. The returned `validators` array contains native JS objects. - validators.length = state.validators.length; - state.validators.getAllReadonlyValues(validators); + validators.reset(); + state.validators.getAllReadonlyValuesIter(validators); + validators.clean(); const validatorCount = validators.length; @@ -266,8 +268,9 @@ export function beforeProcessEpoch( const effectiveBalancesByIncrements = epochCtx.effectiveBalanceIncrements; - for (let i = 0; i < validatorCount; i++) { - const validator = validators[i]; + // for (let i = 0; i < validatorCount; i++) { + let i = 0; + for (const validator of validators) { let flag = 0; if (validator.slashed) { @@ -333,7 +336,7 @@ export function beforeProcessEpoch( // // Use `else` since indicesEligibleForActivationQueue + indicesEligibleForActivation are mutually exclusive else if (validator.activationEpoch === FAR_FUTURE_EPOCH && validator.activationEligibilityEpoch <= currentEpoch) { - indicesEligibleForActivation.push(i); + indicesEligibleForActivation.push({validatorIndex: i, activationEligibilityEpoch: validator.activationEligibilityEpoch}); } // To optimize process_registry_updates(): @@ -358,6 +361,7 @@ export function beforeProcessEpoch( if (isActiveNext2) { nextEpochShufflingActiveValidatorIndices.push(i); } + i++; } if (totalActiveStakeByIncrement < 1) { @@ -372,7 +376,7 @@ export function beforeProcessEpoch( // To optimize process_registry_updates(): // order by sequence of activationEligibilityEpoch setting and then index indicesEligibleForActivation.sort( - (a, b) => validators[a].activationEligibilityEpoch - validators[b].activationEligibilityEpoch || a - b + (a, b) => a.activationEligibilityEpoch - b.activationEligibilityEpoch || a.validatorIndex - b.validatorIndex ); if (forkSeq === ForkSeq.phase0) { @@ -480,7 +484,7 @@ export function beforeProcessEpoch( eligibleValidatorIndices, indicesToSlash, indicesEligibleForActivationQueue, - indicesEligibleForActivation, + indicesEligibleForActivation: indicesEligibleForActivation.map(({validatorIndex}) => validatorIndex), indicesToEject, nextEpochShufflingActiveValidatorIndices, // to be updated in processEffectiveBalanceUpdates diff --git a/packages/state-transition/src/util/balance.ts b/packages/state-transition/src/util/balance.ts index edff9acef134..e14a18ab6606 100644 --- a/packages/state-transition/src/util/balance.ts +++ b/packages/state-transition/src/util/balance.ts @@ -4,6 +4,7 @@ import {bigIntMax} from "@lodestar/utils"; import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js"; import {BeaconStateAllForks} from ".."; import {CachedBeaconStateAllForks} from "../types.js"; +import {ReusableListIterator} from "@chainsafe/ssz"; /** * Return the combined effective balance of the [[indices]]. @@ -46,7 +47,7 @@ export function decreaseBalance(state: BeaconStateAllForks, index: ValidatorInde /** * This data is reused and never gc. */ -const validators = new Array(); +const validators = new ReusableListIterator(); /** * This method is used to get justified balances from a justified state. @@ -68,14 +69,16 @@ export function getEffectiveBalanceIncrementsZeroInactive( validatorCount ); - validators.length = validatorCount; - justifiedState.validators.getAllReadonlyValues(validators); + validators.reset(); + justifiedState.validators.getAllReadonlyValuesIter(validators); + validators.clean(); + let i = 0; let j = 0; - for (let i = 0; i < validatorCount; i++) { + for (const validator of validators) { if (i === activeIndices[j]) { // active validator j++; - if (validators[i].slashed) { + if (validator.slashed) { // slashed validator effectiveBalanceIncrementsZeroInactive[i] = 0; } @@ -83,6 +86,7 @@ export function getEffectiveBalanceIncrementsZeroInactive( // inactive validator effectiveBalanceIncrementsZeroInactive[i] = 0; } + i++; } return effectiveBalanceIncrementsZeroInactive;