Skip to content

Commit

Permalink
feat: minimal alloc viewdu batch hash (#6977)
Browse files Browse the repository at this point in the history
* fix: sync listValidator.ts from ssz

* fix: reuse HashComputationGroup in some flows

* feat: use same HCGroup for epoch transition

* fix: only batch hash balances of state in epoch transition
  • Loading branch information
twoeths authored Jul 24, 2024
1 parent 24d575e commit bff2090
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import {BlockProcessOpts} from "../options.js";
import {byteArrayEquals} from "../../util/bytes.js";
import {nextEventLoop} from "../../util/eventLoop.js";
import {BlockInput, ImportBlockOpts} from "./types.js";
import {HashComputationGroup} from "@chainsafe/persistent-merkle-tree";

/**
* Data in a BeaconBlock is bounded so we can use a single HashComputationGroup for all blocks
*/
const blockHCGroup = new HashComputationGroup();

/**
* Verifies 1 or more blocks are fully valid running the full state transition; from a linear sequence of blocks.
Expand Down Expand Up @@ -63,7 +69,7 @@ export async function verifyBlocksStateTransitionOnly(
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
source: StateHashTreeRootSource.blockTransition,
});
const stateRoot = postState.hashTreeRoot();
const stateRoot = postState.hashTreeRoot(blockHCGroup);
hashTreeRootTimer?.();

// Check state root matches
Expand Down
17 changes: 16 additions & 1 deletion packages/beacon-node/src/chain/prepareNextSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ import {isQueueErrorAborted} from "../util/queue/index.js";
import {prepareExecutionPayload, getPayloadAttributesForSSE} from "./produceBlock/produceBlockBody.js";
import {IBeaconChain} from "./interface.js";
import {RegenCaller} from "./regen/index.js";
import {HashComputationGroup} from "@chainsafe/persistent-merkle-tree";

/* With 12s slot times, this scheduler will run 4s before the start of each slot (`12 / 3 = 4`). */
export const SCHEDULER_LOOKAHEAD_FACTOR = 3;

/* We don't want to do more epoch transition than this */
const PREPARE_EPOCH_LIMIT = 1;

/**
* The same HashComputationGroup to be used for all epoch transition.
*/
const balancesHCGroup = new HashComputationGroup();

/**
* At Bellatrix, if we are responsible for proposing in next slot, we want to prepare payload
* 4s (1/3 slot) before the start of next slot
Expand Down Expand Up @@ -229,7 +235,16 @@ export class PrepareNextSlotScheduler {
const hashTreeRootTimer = this.metrics?.stateHashTreeRootTime.startTimer({
source: isEpochTransition ? StateHashTreeRootSource.prepareNextEpoch : StateHashTreeRootSource.prepareNextSlot,
});
state.hashTreeRoot();
if (isEpochTransition) {
// balances are completely changed per epoch and it's not much different so we can reuse the HashComputationGroup
state.balances.hashTreeRoot(balancesHCGroup);
// it's more performant to use normal hashTreeRoot() for the rest of the state
// this saves ~10ms per ~100ms as monitored on mainnet as of Jul 2024
state.node.rootHashObject;
} else {
// normal slot, not worth to batch hash
state.node.rootHashObject;
}
hashTreeRootTimer?.();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function processParticipationFlagUpdates(state: CachedBeaconStateAltair):
state.currentEpochParticipation.node,
zeroNode(ssz.altair.EpochParticipation.chunkDepth),
state.currentEpochParticipation.length,
null
);

state.currentEpochParticipation = ssz.altair.EpochParticipation.getViewDU(currentEpochParticipationNode);
Expand Down
22 changes: 9 additions & 13 deletions packages/types/src/phase0/viewDU/listValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ByteViews,
ContainerNodeStructTreeViewDU,
} from "@chainsafe/ssz";
import {HashComputationGroup, Node, digestNLevel, setNodesAtDepth} from "@chainsafe/persistent-merkle-tree";
import {HashComputationGroup, HashComputationLevel, Node, digestNLevel, setNodesAtDepth} from "@chainsafe/persistent-merkle-tree";
import {byteArrayIntoHashObject} from "@chainsafe/as-sha256";
import {ValidatorNodeStructType, ValidatorType, validatorToChunkBytes} from "../validator.js";

Expand Down Expand Up @@ -50,10 +50,10 @@ export class ListValidatorTreeViewDU extends ListCompositeTreeViewDU<ValidatorNo
super(type, _rootNode, cache);
}

commit(hashComps: HashComputationGroup | null = null): void {
commit(hcOffset = 0, hcByLevel: HashComputationLevel[] | null = null): void {
const isOldRootHashed = this._rootNode.h0 !== null;
if (this.viewsChanged.size === 0) {
if (!isOldRootHashed && hashComps !== null) {
if (!isOldRootHashed && hcByLevel !== null) {
// not possible to get HashComputations due to BranchNodeStruct
this._rootNode.root;
}
Expand Down Expand Up @@ -125,23 +125,19 @@ export class ListValidatorTreeViewDU extends ListCompositeTreeViewDU<ValidatorNo
const indexes = nodesChanged.map((entry) => entry.index);
const nodes = nodesChanged.map((entry) => entry.node);
const chunksNode = this.type.tree_getChunksNode(this._rootNode);
const hashCompsThis =
hashComps != null && isOldRootHashed
? {
byLevel: hashComps.byLevel,
offset: hashComps.offset + this.type.tree_chunksNodeOffset(),
}
: null;
const newChunksNode = setNodesAtDepth(chunksNode, this.type.chunkDepth, indexes, nodes, hashCompsThis);
const offsetThis = hcOffset + this.type.tree_chunksNodeOffset();
const byLevelThis = hcByLevel != null && isOldRootHashed ? hcByLevel : null;
const newChunksNode = setNodesAtDepth(chunksNode, this.type.chunkDepth, indexes, nodes, offsetThis, byLevelThis);

this._rootNode = this.type.tree_setChunksNode(
this._rootNode,
newChunksNode,
this.dirtyLength ? this._length : null,
hashComps
hcOffset,
hcByLevel
);

if (!isOldRootHashed && hashComps !== null) {
if (!isOldRootHashed && hcByLevel !== null) {
// should never happen, handle just in case
// not possible to get HashComputations due to BranchNodeStruct
this._rootNode.root;
Expand Down

0 comments on commit bff2090

Please sign in to comment.