-
-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: add fork-choice benchmark for updateHead (#5577)
* Add fork-choice benchmark for updateHead * Add more benchmarks
- Loading branch information
Showing
6 changed files
with
253 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 0 additions & 169 deletions
169
packages/fork-choice/test/perf/forkChoice/forkChoice.test.ts
This file was deleted.
Oops, something went wrong.
94 changes: 94 additions & 0 deletions
94
packages/fork-choice/test/perf/forkChoice/onAttestation.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import {itBench} from "@dapplion/benchmark"; | ||
import {AttestationData, IndexedAttestation} from "@lodestar/types/phase0"; | ||
import {ATTESTATION_SUBNET_COUNT} from "@lodestar/params"; | ||
import {ssz} from "@lodestar/types"; | ||
import {computeEpochAtSlot} from "@lodestar/state-transition"; | ||
import {fromHexString, toHexString} from "@chainsafe/ssz"; | ||
import {initializeForkChoice} from "./util.js"; | ||
|
||
describe("ForkChoice onAttestation", () => { | ||
/** | ||
* Committee: | ----------- 0 --------------| ... | ----------------------- i --------------------- | ------------------------63 -------------------------| | ||
* Validator index: | 0 1 2 ... committeeLength-1 | ... | (i*committeeLengh + ) 0 1 2 ... committeeLengh-1| (63*committeeLengh +) 0 1 2 ... committeeLength - 1 | | ||
*/ | ||
itBench({ | ||
id: "pass gossip attestations to forkchoice per slot", | ||
beforeEach: () => { | ||
const initialBlockCount = 64; | ||
const forkchoice = initializeForkChoice({ | ||
initialBlockCount, | ||
initialValidatorCount: 600_000, | ||
initialEquivocatedCount: 0, | ||
}); | ||
const head = forkchoice.updateHead(); | ||
|
||
// at slot 64, forkchoice receives attestations of slot 63 | ||
forkchoice.updateTime(initialBlockCount); | ||
// there are 700 aggregate and proof max per slot | ||
// as of Jan 2022 | ||
const committeeLength = 135; | ||
// considering TARGET_AGGREGATORS_PER_COMMITTEE=16, it's not likely we have more than this number of aggregators | ||
// connect to node per slot | ||
const numAggregatorsConnectedToNode = 3; | ||
const attestationDataOmitIndex: Omit<AttestationData, "index"> = { | ||
beaconBlockRoot: fromHexString(head.blockRoot), | ||
slot: initialBlockCount - 1, | ||
source: { | ||
epoch: head.justifiedEpoch, | ||
root: fromHexString(head.justifiedRoot), | ||
}, | ||
target: { | ||
epoch: computeEpochAtSlot(head.slot), | ||
root: fromHexString(head.targetRoot), | ||
}, | ||
}; | ||
|
||
// unaggregatedAttestations: aggregator {i} for committee index {i} | ||
const unaggregatedAttestations: IndexedAttestation[] = []; | ||
for (let committeeIndex = 0; committeeIndex < numAggregatorsConnectedToNode; committeeIndex++) { | ||
const attestationData: AttestationData = { | ||
...attestationDataOmitIndex, | ||
index: committeeIndex, | ||
}; | ||
for (let i = 0; i < committeeLength; i++) { | ||
const validatorIndex = committeeIndex * committeeLength + i; | ||
unaggregatedAttestations.push({ | ||
attestingIndices: [validatorIndex], | ||
data: attestationData, | ||
signature: Buffer.alloc(96), | ||
}); | ||
} | ||
} | ||
|
||
// aggregated attestations: each committee index has 11 aggregators in average | ||
// 64 committee indices map to 704 aggregated attestations per slot | ||
const aggregatedAttestations: IndexedAttestation[] = []; | ||
const averageAggregatorsPerSlot = 11; | ||
for (let committeeIndex = 0; committeeIndex < ATTESTATION_SUBNET_COUNT; committeeIndex++) { | ||
const tbAttestationData = { | ||
...attestationDataOmitIndex, | ||
index: committeeIndex, | ||
}; | ||
|
||
// cache the root | ||
ssz.phase0.AttestationData.hashTreeRoot(tbAttestationData); | ||
|
||
for (let aggregator = 0; aggregator < averageAggregatorsPerSlot; aggregator++) { | ||
// same data, different signatures | ||
aggregatedAttestations.push({ | ||
attestingIndices: Array.from({length: committeeLength}, (_, i) => committeeIndex * committeeLength + i), | ||
data: tbAttestationData, | ||
signature: Buffer.alloc(96, aggregator), | ||
}); | ||
} | ||
} | ||
|
||
return {forkchoice, allAttestationsPerSlot: [...unaggregatedAttestations, ...aggregatedAttestations]}; | ||
}, | ||
fn: ({forkchoice, allAttestationsPerSlot}) => { | ||
for (const attestation of allAttestationsPerSlot) { | ||
forkchoice.onAttestation(attestation, toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data))); | ||
} | ||
}, | ||
}); | ||
}); |
66 changes: 66 additions & 0 deletions
66
packages/fork-choice/test/perf/forkChoice/updateHead.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import {itBench} from "@dapplion/benchmark"; | ||
import {computeEpochAtSlot} from "@lodestar/state-transition"; | ||
import {ForkChoice, ProtoBlock} from "../../../src/index.js"; | ||
import {initializeForkChoice, Opts} from "./util.js"; | ||
|
||
describe("forkchoice updateHead", () => { | ||
for (const initialValidatorCount of [100_000, 600_000, 1_000_000]) { | ||
runUpdateHeadBenchmark({initialValidatorCount, initialBlockCount: 64, initialEquivocatedCount: 0}); | ||
} | ||
|
||
for (const initialBlockCount of [ | ||
// 10 epochs of blocks ~ 1 hour | ||
10 * 32, | ||
// 4 hours of blocks | ||
(4 * 60 * 60) / 12, | ||
// // 1 day of blocks | ||
// (24 * 60 * 60) / 12, | ||
// // 20 days of blocks | ||
// (20 * 24 * 60 * 60) / 12, | ||
]) { | ||
runUpdateHeadBenchmark({initialValidatorCount: 600_000, initialBlockCount, initialEquivocatedCount: 0}); | ||
} | ||
|
||
for (const initialEquivocatedCount of [1_000, 10_000, 300_000]) { | ||
runUpdateHeadBenchmark({initialValidatorCount: 600_000, initialBlockCount: 64, initialEquivocatedCount}); | ||
} | ||
|
||
function runUpdateHeadBenchmark(opts: Opts): void { | ||
itBench({ | ||
id: `forkChoice updateHead vc ${opts.initialValidatorCount} bc ${opts.initialBlockCount} eq ${opts.initialEquivocatedCount}`, | ||
before: () => { | ||
const forkChoice = initializeForkChoice(opts); | ||
|
||
const vote1 = forkChoice.updateHead(); | ||
const vote2 = forkChoice.getBlockHex(vote1.parentRoot); | ||
if (!vote2) throw Error("no vote2"); | ||
if (vote1.blockRoot === vote2.blockRoot) throw Error("blockRoot vote1 == vote2"); | ||
if (vote1.slot === vote2.slot) throw Error("slot vote1 == vote2"); | ||
|
||
return { | ||
forkChoice, | ||
vote1, | ||
vote2, | ||
currentVoteIs1: true, | ||
}; | ||
}, | ||
beforeEach: (data) => { | ||
// Flip all votes every run | ||
everyoneVotes(data.currentVoteIs1 ? data.vote2 : data.vote1, data.forkChoice); | ||
data.currentVoteIs1 = !data.currentVoteIs1; | ||
return data; | ||
}, | ||
fn: ({forkChoice}) => { | ||
forkChoice.updateHead(); | ||
}, | ||
}); | ||
} | ||
}); | ||
|
||
function everyoneVotes(vote: ProtoBlock, forkChoice: ForkChoice): void { | ||
const nextEpoch = computeEpochAtSlot(vote.slot); | ||
const nextRoot = vote.blockRoot; | ||
for (let i = 0; i < forkChoice["balances"].length; i++) { | ||
forkChoice["addLatestMessage"](i, nextEpoch, nextRoot); | ||
} | ||
} |
Oops, something went wrong.