Skip to content

Commit

Permalink
Add opPool benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
nazarhussain committed Nov 30, 2023
1 parent 228d7ba commit 2ea45a3
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const SYNC_TOLERANCE_EPOCHS = 1;
* Cutoff time to wait for execution and builder block production apis to resolve
* Post this time, race execution and builder to pick whatever resolves first
*
* Emprically the builder block resolves in ~1.5+ seconds, and executon should resolve <1 sec.
* Empirically the builder block resolves in ~1.5+ seconds, and execution should resolve <1 sec.
* So lowering the cutoff to 2 sec from 3 seconds to publish faster for successful proposal
* as proposals post 4 seconds into the slot seems to be not being included
*/
Expand Down Expand Up @@ -437,7 +437,7 @@ export function getValidatorApi({
chain.executionBuilder !== undefined &&
builderSelection !== routes.validator.BuilderSelection.ExecutionOnly;

logger.verbose("produceBlockV3 assembling block", {
logger.verbose("Assembling block with produceBlockV3 ", {
fork,
builderSelection,
slot,
Expand Down
24 changes: 24 additions & 0 deletions packages/beacon-node/test/fixtures/capella.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {CachedBeaconStateAltair} from "@lodestar/state-transition";
import {capella} from "@lodestar/types";

export function generateBlsToExecutionChanges(
state: CachedBeaconStateAltair,
count: number
): capella.SignedBLSToExecutionChange[] {
const result: capella.SignedBLSToExecutionChange[] = [];

for (const validatorIndex of state.epochCtx.proposers) {
result.push({
message: {
fromBlsPubkey: state.epochCtx.index2pubkey[validatorIndex].toBytes(),
toExecutionAddress: Buffer.alloc(20),
validatorIndex,
},
signature: Buffer.alloc(96),
});

if (result.length >= count) return result;
}

return result;
}
98 changes: 98 additions & 0 deletions packages/beacon-node/test/fixtures/phase0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {
CachedBeaconStateAltair,
computeEpochAtSlot,
computeStartSlotAtEpoch,
getBlockRootAtSlot,
} from "@lodestar/state-transition";
import {phase0} from "@lodestar/types";

export function generateIndexedAttestations(
state: CachedBeaconStateAltair,
count: number
): phase0.IndexedAttestation[] {
const result: phase0.IndexedAttestation[] = [];

for (let epochSlot = 0; epochSlot < SLOTS_PER_EPOCH; epochSlot++) {
const slot = state.slot - 1 - epochSlot;
const epoch = computeEpochAtSlot(slot);
const committeeCount = state.epochCtx.getCommitteeCountPerSlot(epoch);

for (let committeeIndex = 0; committeeIndex < committeeCount; committeeIndex++) {
result.push({
attestingIndices: state.epochCtx.getBeaconCommittee(slot, committeeIndex),
data: {
slot: slot,
index: committeeIndex,
beaconBlockRoot: getBlockRootAtSlot(state, slot),
source: {
epoch: state.currentJustifiedCheckpoint.epoch,
root: state.currentJustifiedCheckpoint.root,
},
target: {
epoch: epoch,
root: getBlockRootAtSlot(state, computeStartSlotAtEpoch(epoch)),
},
},
signature: Buffer.alloc(96),
});

if (result.length >= count) return result;
}
}

return result;
}

export function generateBeaconBlockHeader(state: CachedBeaconStateAltair, count: number): phase0.BeaconBlockHeader[] {
const headers: phase0.BeaconBlockHeader[] = [];

for (let i = 1; i <= count; i++) {
const slot = state.slot - i;
const epoch = computeEpochAtSlot(slot);
const epochStartSlot = computeStartSlotAtEpoch(epoch);
const parentRoot = getBlockRootAtSlot(state, slot - 1);
const stateRoot = getBlockRootAtSlot(state, epochStartSlot);
const bodyRoot = getBlockRootAtSlot(state, epochStartSlot + 1);
const header: phase0.BeaconBlockHeader = {
slot,
proposerIndex: state.epochCtx.proposers[slot % SLOTS_PER_EPOCH],
parentRoot,
stateRoot,
bodyRoot,
};

headers.push(header);
}
return headers;
}

export function generateSignedBeaconBlockHeader(
state: CachedBeaconStateAltair,
count: number
): phase0.SignedBeaconBlockHeader[] {
const headers = generateBeaconBlockHeader(state, count);

return headers.map((header) => ({
message: header,
signature: Buffer.alloc(96),
}));
}

export function generateVoluntaryExits(state: CachedBeaconStateAltair, count: number): phase0.SignedVoluntaryExit[] {
const result: phase0.SignedVoluntaryExit[] = [];

for (const validatorIndex of state.epochCtx.proposers) {
result.push({
message: {
epoch: state.currentJustifiedCheckpoint.epoch,
validatorIndex,
},
signature: Buffer.alloc(96),
});

if (result.length >= count) return result;
}

return result;
}
109 changes: 109 additions & 0 deletions packages/beacon-node/test/perf/chain/opPools/opPool.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {itBench} from "@dapplion/benchmark";
import {
MAX_ATTESTER_SLASHINGS,
MAX_BLS_TO_EXECUTION_CHANGES,
MAX_PROPOSER_SLASHINGS,
MAX_VOLUNTARY_EXITS,
} from "@lodestar/params";
import {CachedBeaconStateAltair} from "@lodestar/state-transition";
import {ssz} from "@lodestar/types";
// eslint-disable-next-line import/no-relative-packages
import {generatePerfTestCachedStateAltair} from "../../../../../state-transition/test/perf/util.js";
import {OpPool} from "../../../../src/chain/opPools/opPool.js";
import {generateBlsToExecutionChanges} from "../../../fixtures/capella.js";
import {
generateIndexedAttestations,
generateSignedBeaconBlockHeader,
generateVoluntaryExits,
} from "../../../fixtures/phase0.js";

// Aug 11 2021
// getAttestationsForBlock
// ✓ getAttestationsForBlock 4.410948 ops/s 226.7086 ms/op - 64 runs 51.8 s
describe("opPool", () => {
let originalState: CachedBeaconStateAltair;

before(function () {
this.timeout(2 * 60 * 1000); // Generating the states for the first time is very slow

originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true});
});

itBench({
id: "getSlashingsAndExits - default max",
beforeEach: () => {
const pool = new OpPool();
fillAttesterSlashing(pool, originalState, MAX_ATTESTER_SLASHINGS);
fillProposerSlashing(pool, originalState, MAX_PROPOSER_SLASHINGS);
fillVoluntaryExits(pool, originalState, MAX_VOLUNTARY_EXITS);
fillBlsToExecutionChanges(pool, originalState, MAX_BLS_TO_EXECUTION_CHANGES);

return pool;
},
fn: (pool) => {
pool.getSlashingsAndExits(originalState);
},
});

itBench({
id: "getSlashingsAndExits - 2k",
beforeEach: () => {
const pool = new OpPool();
const maxItemsInPool = 2_000;

fillAttesterSlashing(pool, originalState, maxItemsInPool);
fillProposerSlashing(pool, originalState, maxItemsInPool);
fillVoluntaryExits(pool, originalState, maxItemsInPool);
fillBlsToExecutionChanges(pool, originalState, maxItemsInPool);

return pool;
},
fn: (pool) => {
pool.getSlashingsAndExits(originalState);
},
});
});

function fillAttesterSlashing(pool: OpPool, state: CachedBeaconStateAltair, count: number): OpPool {
for (const attestation of generateIndexedAttestations(state, count)) {
pool.insertAttesterSlashing({
attestation1: ssz.phase0.IndexedAttestationBigint.fromJson(ssz.phase0.IndexedAttestation.toJson(attestation)),
attestation2: ssz.phase0.IndexedAttestationBigint.fromJson(ssz.phase0.IndexedAttestation.toJson(attestation)),
});
}

return pool;
}

function fillProposerSlashing(pool: OpPool, state: CachedBeaconStateAltair, count: number): OpPool {
for (const blockHeader of generateSignedBeaconBlockHeader(state, count)) {
pool.insertProposerSlashing({
signedHeader1: ssz.phase0.SignedBeaconBlockHeaderBigint.fromJson(
ssz.phase0.SignedBeaconBlockHeader.toJson(blockHeader)
),
signedHeader2: ssz.phase0.SignedBeaconBlockHeaderBigint.fromJson(
ssz.phase0.SignedBeaconBlockHeader.toJson(blockHeader)
),
});
}

return pool;
}

function fillVoluntaryExits(pool: OpPool, state: CachedBeaconStateAltair, count: number): OpPool {
for (const exit of generateVoluntaryExits(state, count)) {
pool.insertVoluntaryExit(exit);
}

return pool;
}

// This does not set the `withdrawalCredentials` for the validator
// So it will be in the pool but not returned from `getSlashingsAndExits`
function fillBlsToExecutionChanges(pool: OpPool, state: CachedBeaconStateAltair, count: number): OpPool {
for (const blsToExecution of generateBlsToExecutionChanges(state, count)) {
pool.insertBlsToExecutionChange(blsToExecution);
}

return pool;
}

0 comments on commit 2ea45a3

Please sign in to comment.