diff --git a/packages/ssz/test/lodestarTypes/phase0/sszTypes.ts b/packages/ssz/test/lodestarTypes/phase0/sszTypes.ts index 119f9eb1..6e124d45 100644 --- a/packages/ssz/test/lodestarTypes/phase0/sszTypes.ts +++ b/packages/ssz/test/lodestarTypes/phase0/sszTypes.ts @@ -256,7 +256,7 @@ export const Validator = new ContainerType( ); // Export as stand-alone for direct tree optimizations -export const Validators = new ListCompositeType(Validator, VALIDATOR_REGISTRY_LIMIT); +export const Validators = new ListCompositeType(ValidatorNodeStruct, VALIDATOR_REGISTRY_LIMIT); export const Balances = new ListBasicType(UintNumber64, VALIDATOR_REGISTRY_LIMIT); export const RandaoMixes = new VectorCompositeType(Bytes32, EPOCHS_PER_HISTORICAL_VECTOR); export const Slashings = new VectorBasicType(Gwei, EPOCHS_PER_SLASHINGS_VECTOR); diff --git a/packages/ssz/test/perf/deserializationEth2.test.ts b/packages/ssz/test/perf/deserializationEth2.test.ts deleted file mode 100644 index 79599bb7..00000000 --- a/packages/ssz/test/perf/deserializationEth2.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {itBench} from "@dapplion/benchmark"; -import * as sszPhase0 from "../lodestarTypes/phase0/sszTypes"; -import * as sszAltair from "../lodestarTypes/altair/sszTypes"; -import {getAttestation, getSignedAggregateAndProof, getSignedContributionAndProof} from "../utils/generateEth2Objs"; - -describe("Deserialization of frequent eth2 objects", () => { - itBench({ - id: "deserialize Attestation - struct", - before: () => sszPhase0.Attestation.serialize(getAttestation(0)), - beforeEach: (bytes) => bytes, - fn: (bytes) => { - sszPhase0.Attestation.deserialize(bytes); - }, - }); - - itBench({ - id: "deserialize Attestation - tree", - before: () => sszPhase0.Attestation.serialize(getAttestation(0)), - beforeEach: (bytes) => bytes, - fn: (bytes) => { - sszPhase0.Attestation.deserializeToView(bytes); - }, - }); - - itBench({ - id: "deserialize SignedAggregateAndProof - struct", - before: () => sszPhase0.SignedAggregateAndProof.serialize(getSignedAggregateAndProof(0)), - beforeEach: (bytes) => bytes, - fn: (bytes) => { - sszPhase0.SignedAggregateAndProof.deserialize(bytes); - }, - }); - - itBench({ - id: "deserialize SignedAggregateAndProof - tree", - before: () => sszPhase0.SignedAggregateAndProof.serialize(getSignedAggregateAndProof(0)), - beforeEach: (bytes) => bytes, - fn: (bytes) => { - sszPhase0.SignedAggregateAndProof.deserializeToView(bytes); - }, - }); - - itBench({ - id: "deserialize SignedContributionAndProof - struct", - before: () => sszAltair.SignedContributionAndProof.serialize(getSignedContributionAndProof(0)), - beforeEach: (bytes) => bytes, - fn: (bytes) => { - sszAltair.SignedContributionAndProof.deserialize(bytes); - }, - }); -}); diff --git a/packages/ssz/test/perf/deserializeEth2.test.ts b/packages/ssz/test/perf/deserializeEth2.test.ts new file mode 100644 index 00000000..323c334d --- /dev/null +++ b/packages/ssz/test/perf/deserializeEth2.test.ts @@ -0,0 +1,104 @@ +import {itBench} from "@dapplion/benchmark"; +import {BeaconState} from "../lodestarTypes/altair/types"; +import * as sszPhase0 from "../lodestarTypes/phase0/sszTypes"; +import * as sszAltair from "../lodestarTypes/altair/sszTypes"; +import { + getAttestation, + getOnce, + getRandomState, + getSignedAggregateAndProof, + getSignedContributionAndProof, +} from "../utils/generateEth2Objs"; +import {TreeViewDU} from "../../src"; + +describe("Deserialize frequent eth2 objects", () => { + itBench({ + id: "deserialize Attestation - struct", + before: () => sszPhase0.Attestation.serialize(getAttestation(0)), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszPhase0.Attestation.deserialize(bytes); + }, + }); + + itBench({ + id: "deserialize Attestation - tree", + before: () => sszPhase0.Attestation.serialize(getAttestation(0)), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszPhase0.Attestation.deserializeToView(bytes); + }, + }); + + itBench({ + id: "deserialize SignedAggregateAndProof - struct", + before: () => sszPhase0.SignedAggregateAndProof.serialize(getSignedAggregateAndProof(0)), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszPhase0.SignedAggregateAndProof.deserialize(bytes); + }, + }); + + itBench({ + id: "deserialize SignedAggregateAndProof - tree", + before: () => sszPhase0.SignedAggregateAndProof.serialize(getSignedAggregateAndProof(0)), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszPhase0.SignedAggregateAndProof.deserializeToView(bytes); + }, + }); + + itBench({ + id: "deserialize SignedContributionAndProof - struct", + before: () => sszAltair.SignedContributionAndProof.serialize(getSignedContributionAndProof(0)), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszAltair.SignedContributionAndProof.deserialize(bytes); + }, + }); + + for (const validatorCount of [300_000]) { + // Compute once for all benchmarks only if run + const getStateVc = getOnce(() => getRandomState(validatorCount)); + const getStateViewDU = getOnce(() => sszAltair.BeaconState.toViewDU(getStateVc())); + + itBench({ + id: `BeaconState vc ${validatorCount} - deserialize tree`, + before: () => getStateViewDU().serialize(), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + sszAltair.BeaconState.deserializeToViewDU(bytes); + }, + }); + + itBench({ + id: `BeaconState vc ${validatorCount} - serialize tree`, + fn: () => { + getStateViewDU().serialize(); + }, + }); + + for (const {fieldName, fieldType} of sszAltair.BeaconState.fieldsEntries) { + // Only benchmark big data structures + if (fieldType.maxSize < 10e6 || fieldType.isBasic) { + continue; + } + + itBench({ + id: `BeaconState.${fieldName} vc ${validatorCount} - deserialize tree`, + before: () => (getStateViewDU()[fieldName] as TreeViewDU).serialize(), + beforeEach: (bytes) => bytes, + fn: (bytes) => { + fieldType.deserializeToViewDU(bytes); + }, + }); + + itBench({ + id: `BeaconState.${fieldName} vc ${validatorCount} - serialize tree`, + fn: () => { + (getStateViewDU()[fieldName] as TreeViewDU).serialize(); + }, + }); + } + } +}); diff --git a/packages/ssz/test/utils/generateEth2Objs.ts b/packages/ssz/test/utils/generateEth2Objs.ts index 697ee321..a6a58c89 100644 --- a/packages/ssz/test/utils/generateEth2Objs.ts +++ b/packages/ssz/test/utils/generateEth2Objs.ts @@ -1,11 +1,33 @@ +import * as sszAltair from "../lodestarTypes/altair/sszTypes"; import {Attestation, SignedAggregateAndProof, SignedBeaconBlock} from "../lodestarTypes/phase0/types"; -import {SignedContributionAndProof} from "../lodestarTypes/altair/types"; +import {SignedContributionAndProof, BeaconState} from "../lodestarTypes/altair/types"; import {BitArray} from "../../src"; // Typical mainnet numbers const BITS_PER_ATTESTATION = 90; const ATTESTATIONS_PER_BLOCK = 90; +export function getRandomState(validatorCount: number): BeaconState { + const state = sszAltair.BeaconState.defaultValue; + for (let i = 0; i < validatorCount; i++) { + state.balances.push(34788813514 + i); + state.currentEpochParticipation.push(3); + state.previousEpochParticipation.push(7); + state.inactivityScores.push(0); + state.validators.push({ + pubkey: new Uint8Array(48), + withdrawalCredentials: new Uint8Array(32), + effectiveBalance: 32e9, + slashed: false, + activationEligibilityEpoch: i, + activationEpoch: i, + exitEpoch: Infinity, + withdrawableEpoch: Infinity, + }); + } + return state; +} + export function getAttestation(i: number): Attestation { return { aggregationBits: getBitsSingle(120, i % 120), @@ -108,3 +130,13 @@ function randomBytes(bytes: number): Uint8Array { return uint8Arr; } + +export function getOnce(fn: () => T): () => T { + let value: T | null = null; + return function () { + if (value === null) { + value = fn(); + } + return value; + }; +}