Skip to content

Commit c43b070

Browse files
authored
chore: update the performance tests to use @chainsafe/benchmark (#7373)
1 parent f9d080c commit c43b070

File tree

76 files changed

+938
-906
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+938
-906
lines changed

.benchrc.yaml

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
# Mocha opts
2-
extension: ["ts"]
3-
colors: true
4-
node-option:
5-
- "loader=ts-node/register"
6-
71
# benchmark opts
82
threshold: 3
9-
maxMs: 60_000
3+
maxMs: 60000
104
minRuns: 10
5+
# Default is set to 0.005, which is too low considering the benchmark setup we have
6+
# Changing it to 0.05 which is 5/100, so 5% difference of moving average among run times
7+
convergeFactor: 0.075 # 7.5 / 100
8+
triggerGC: false
9+
sort: true
10+
convergence: linear
11+
averageCalculation: clean-outliers

biome.jsonc

-8
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,6 @@
255255
}
256256
}
257257
},
258-
// Dependencies still using mocha
259-
{
260-
"include": ["packages/**/test/perf/**/*.test.ts", "packages/state-transition/test/utils/beforeValueMocha.ts"],
261-
"javascript": {
262-
// These are used by mocha
263-
"globals": ["describe", "it", "before", "after"]
264-
}
265-
},
266258
{
267259
"include": [
268260
// These files are using mix cases e.g. `engine_newPayloadV4`

package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@
4848
},
4949
"devDependencies": {
5050
"@actions/core": "^1.10.1",
51-
"@dapplion/benchmark": "^0.2.4",
51+
"@chainsafe/benchmark": "^1.2.3",
5252
"@biomejs/biome": "^1.9.3",
53-
"@types/mocha": "^10.0.6",
5453
"@types/node": "^20.12.8",
5554
"@vitest/browser": "^2.0.4",
5655
"@vitest/coverage-v8": "^2.0.4",
@@ -61,7 +60,6 @@
6160
"jsdom": "^23.0.1",
6261
"lerna": "^7.3.0",
6362
"libp2p": "1.4.3",
64-
"mocha": "^10.2.0",
6563
"node-gyp": "^9.4.0",
6664
"npm-run-all": "^4.1.5",
6765
"path-browserify": "^1.0.1",

packages/api/test/perf/compileRouteUrlFormater.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import {bench, describe} from "@chainsafe/benchmark";
12
import {compileRouteUrlFormatter} from "../../src/utils/urlFormat.js";
23

34
describe("route parse", () => {
4-
it.skip("Benchmark compileRouteUrlFormatter", () => {
5+
bench.skip("Benchmark compileRouteUrlFormatter", () => {
56
const path = "/eth/v1/validator/:name/attester/:epoch";
67
const args = {epoch: 5, name: "HEAD"};
78

packages/beacon-node/test/e2e/sync/finalizedSync.test.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import {ChainConfig} from "@lodestar/config";
55
import {TimestampFormatCode} from "@lodestar/logger";
66
import {SLOTS_PER_EPOCH} from "@lodestar/params";
77
import {phase0} from "@lodestar/types";
8-
import {assert} from "chai";
9-
import {afterEach, describe, it, vi} from "vitest";
8+
import {afterEach, describe, expect, it, vi} from "vitest";
109
import {ChainEvent} from "../../../src/chain/index.js";
1110
import {waitForEvent} from "../../utils/events/resolver.js";
1211
import {LogLevel, TestLoggerOpts, testLogger} from "../../utils/logger.js";
@@ -114,7 +113,7 @@ describe("sync / finalized sync", () => {
114113
await waitForSynced;
115114
loggerNodeB.info("Node B synced to Node A, received head block", {slot: head.message.slot});
116115
} catch (_e) {
117-
assert.fail("Failed to sync to other node in time");
116+
expect.fail("Failed to sync to other node in time");
118117
}
119118
});
120119
});

packages/beacon-node/test/perf/api/impl/validator/attester.test.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {itBench} from "@dapplion/benchmark";
1+
import {beforeAll, bench, describe} from "@chainsafe/benchmark";
22
import {generatePerfTestCachedStatePhase0, numValidators} from "../../../../../../state-transition/test/perf/util.js";
33
import {getPubkeysForIndices} from "../../../../../src/api/impl/validator/utils.js";
44
import {linspace} from "../../../../../src/util/numpy.js";
@@ -20,15 +20,14 @@ import {linspace} from "../../../../../src/util/numpy.js";
2020
describe("api / impl / validator", () => {
2121
let state: ReturnType<typeof generatePerfTestCachedStatePhase0>;
2222

23-
before(function () {
24-
this.timeout(60 * 1000);
23+
beforeAll(() => {
2524
state = generatePerfTestCachedStatePhase0();
2625
});
2726

2827
const reqCounts = process.env.CI ? [1000] : [1, 100, 1000];
2928

3029
for (const reqCount of reqCounts) {
31-
itBench({
30+
bench({
3231
id: `getPubkeys - index2pubkey - req ${reqCount} vs - ${numValidators} vc`,
3332
noThreshold: true,
3433
fn: () => {
@@ -42,7 +41,7 @@ describe("api / impl / validator", () => {
4241

4342
// 7.17 ms / op (1000)
4443
for (const reqCount of reqCounts) {
45-
itBench({
44+
bench({
4645
id: `getPubkeys - validatorsArr - req ${reqCount} vs - ${numValidators} vc`,
4746
// Only track regressions for 1000 in CI to ensure performance does not degrade
4847
noThreshold: reqCount < 1000,

packages/beacon-node/test/perf/bls/bls.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import crypto from "node:crypto";
2+
import {bench, describe} from "@chainsafe/benchmark";
23
import {
34
PublicKey,
45
SecretKey,
@@ -8,7 +9,6 @@ import {
89
verify,
910
verifyMultipleAggregateSignatures,
1011
} from "@chainsafe/blst";
11-
import {itBench} from "@dapplion/benchmark";
1212
import {linspace} from "../../../src/util/numpy.js";
1313

1414
describe("BLS ops", () => {
@@ -60,15 +60,15 @@ describe("BLS ops", () => {
6060
}
6161

6262
// Note: getSet() caches the value, does not re-compute every time
63-
itBench({id: "BLS verify - blst", beforeEach: () => getSet(0)}, (set) => {
63+
bench({id: "BLS verify - blst", beforeEach: () => getSet(0)}, (set) => {
6464
const isValid = verify(set.message, set.publicKey, Signature.fromBytes(set.signature));
6565
if (!isValid) throw Error("Invalid");
6666
});
6767

6868
// An aggregate and proof object has 3 signatures.
6969
// We may want to bundle up to 32 sets in a single batch.
7070
for (const count of [3, 8, 32, 64, 128]) {
71-
itBench({
71+
bench({
7272
id: `BLS verifyMultipleSignatures ${count} - blst`,
7373
beforeEach: () => linspace(0, count - 1).map((i) => getSet(i)),
7474
fn: (sets) => {
@@ -88,7 +88,7 @@ describe("BLS ops", () => {
8888
// ideally we want to track 700_000, 1_400_000, 2_100_000 validators but it takes too long
8989
for (const numValidators of [10_000, 100_000]) {
9090
const signatures = linspace(0, numValidators - 1).map((i) => getSet(i % 256).signature);
91-
itBench({
91+
bench({
9292
id: `BLS deserializing ${numValidators} signatures`,
9393
fn: () => {
9494
for (const signature of signatures) {
@@ -103,7 +103,7 @@ describe("BLS ops", () => {
103103
// We may want to bundle up to 32 sets in a single batch.
104104
// TODO: figure out why it does not work with 256 or more
105105
for (const count of [3, 8, 32, 64, 128]) {
106-
itBench({
106+
bench({
107107
id: `BLS verifyMultipleSignatures - same message - ${count} - blst`,
108108
beforeEach: () => linspace(0, count - 1).map((i) => getSetSameMessage(i)),
109109
fn: (sets) => {
@@ -118,7 +118,7 @@ describe("BLS ops", () => {
118118

119119
// Attestations in Mainnet contain 128 max on average
120120
for (const count of [32, 128]) {
121-
itBench({
121+
bench({
122122
id: `BLS aggregatePubkeys ${count} - blst`,
123123
beforeEach: () => linspace(0, count - 1).map((i) => getKeypair(i).publicKey),
124124
fn: (pubkeys) => {

packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts

+78-77
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import {beforeAll, bench, describe} from "@chainsafe/benchmark";
12
import {BitArray, toHexString} from "@chainsafe/ssz";
2-
import {itBench} from "@dapplion/benchmark";
33
import {DataAvailabilityStatus, ExecutionStatus, ForkChoice, IForkChoiceStore, ProtoArray} from "@lodestar/fork-choice";
44
import {HISTORICAL_ROOTS_LIMIT, SLOTS_PER_EPOCH} from "@lodestar/params";
55
import {
@@ -30,57 +30,30 @@ describe(`getAttestationsForBlock vc=${vc}`, () => {
3030
let protoArray: ProtoArray;
3131
let forkchoice: ForkChoice;
3232

33-
before(function () {
34-
this.timeout(5 * 60 * 1000); // Generating the states for the first time is very slow
33+
beforeAll(
34+
() => {
35+
originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true, vc});
3536

36-
originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true, vc});
37-
38-
const {blockHeader, checkpoint} = computeAnchorCheckpoint(originalState.config, originalState);
39-
// TODO figure out why getBlockRootAtSlot(originalState, justifiedSlot) is not the same to justifiedCheckpoint.root
40-
const finalizedEpoch = originalState.finalizedCheckpoint.epoch;
41-
const finalizedCheckpoint = {
42-
epoch: finalizedEpoch,
43-
root: getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(finalizedEpoch)),
44-
};
45-
const justifiedEpoch = originalState.currentJustifiedCheckpoint.epoch;
46-
const justifiedCheckpoint = {
47-
epoch: justifiedEpoch,
48-
root: getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(justifiedEpoch)),
49-
};
50-
51-
protoArray = ProtoArray.initialize(
52-
{
53-
slot: blockHeader.slot,
54-
parentRoot: toHexString(blockHeader.parentRoot),
55-
stateRoot: toHexString(blockHeader.stateRoot),
56-
blockRoot: toHexString(checkpoint.root),
57-
58-
justifiedEpoch: justifiedCheckpoint.epoch,
59-
justifiedRoot: toHexString(justifiedCheckpoint.root),
60-
finalizedEpoch: finalizedCheckpoint.epoch,
61-
finalizedRoot: toHexString(finalizedCheckpoint.root),
62-
unrealizedJustifiedEpoch: justifiedCheckpoint.epoch,
63-
unrealizedJustifiedRoot: toHexString(justifiedCheckpoint.root),
64-
unrealizedFinalizedEpoch: finalizedCheckpoint.epoch,
65-
unrealizedFinalizedRoot: toHexString(finalizedCheckpoint.root),
66-
executionPayloadBlockHash: null,
67-
executionStatus: ExecutionStatus.PreMerge,
68-
69-
timeliness: false,
70-
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
71-
},
72-
originalState.slot
73-
);
37+
const {blockHeader, checkpoint} = computeAnchorCheckpoint(originalState.config, originalState);
38+
// TODO figure out why getBlockRootAtSlot(originalState, justifiedSlot) is not the same to justifiedCheckpoint.root
39+
const finalizedEpoch = originalState.finalizedCheckpoint.epoch;
40+
const finalizedCheckpoint = {
41+
epoch: finalizedEpoch,
42+
root: getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(finalizedEpoch)),
43+
};
44+
const justifiedEpoch = originalState.currentJustifiedCheckpoint.epoch;
45+
const justifiedCheckpoint = {
46+
epoch: justifiedEpoch,
47+
root: getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(justifiedEpoch)),
48+
};
7449

75-
for (let slot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch); slot < originalState.slot; slot++) {
76-
const epoch = computeEpochAtSlot(slot);
77-
protoArray.onBlock(
50+
protoArray = ProtoArray.initialize(
7851
{
79-
slot,
80-
blockRoot: toHexString(getBlockRootAtSlot(originalState, slot)),
81-
parentRoot: toHexString(getBlockRootAtSlot(originalState, slot - 1)),
82-
stateRoot: toHexString(originalState.stateRoots.get(slot % HISTORICAL_ROOTS_LIMIT)),
83-
targetRoot: toHexString(getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(epoch))),
52+
slot: blockHeader.slot,
53+
parentRoot: toHexString(blockHeader.parentRoot),
54+
stateRoot: toHexString(blockHeader.stateRoot),
55+
blockRoot: toHexString(checkpoint.root),
56+
8457
justifiedEpoch: justifiedCheckpoint.epoch,
8558
justifiedRoot: toHexString(justifiedCheckpoint.root),
8659
finalizedEpoch: finalizedCheckpoint.epoch,
@@ -91,36 +64,64 @@ describe(`getAttestationsForBlock vc=${vc}`, () => {
9164
unrealizedFinalizedRoot: toHexString(finalizedCheckpoint.root),
9265
executionPayloadBlockHash: null,
9366
executionStatus: ExecutionStatus.PreMerge,
67+
9468
timeliness: false,
9569
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
9670
},
97-
slot
71+
originalState.slot
9872
);
99-
}
10073

101-
let totalBalance = 0;
102-
for (let i = 0; i < originalState.epochCtx.effectiveBalanceIncrements.length; i++) {
103-
totalBalance += originalState.epochCtx.effectiveBalanceIncrements[i];
104-
}
74+
for (let slot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch); slot < originalState.slot; slot++) {
75+
const epoch = computeEpochAtSlot(slot);
76+
protoArray.onBlock(
77+
{
78+
slot,
79+
blockRoot: toHexString(getBlockRootAtSlot(originalState, slot)),
80+
parentRoot: toHexString(getBlockRootAtSlot(originalState, slot - 1)),
81+
stateRoot: toHexString(originalState.stateRoots.get(slot % HISTORICAL_ROOTS_LIMIT)),
82+
targetRoot: toHexString(getBlockRootAtSlot(originalState, computeStartSlotAtEpoch(epoch))),
83+
justifiedEpoch: justifiedCheckpoint.epoch,
84+
justifiedRoot: toHexString(justifiedCheckpoint.root),
85+
finalizedEpoch: finalizedCheckpoint.epoch,
86+
finalizedRoot: toHexString(finalizedCheckpoint.root),
87+
unrealizedJustifiedEpoch: justifiedCheckpoint.epoch,
88+
unrealizedJustifiedRoot: toHexString(justifiedCheckpoint.root),
89+
unrealizedFinalizedEpoch: finalizedCheckpoint.epoch,
90+
unrealizedFinalizedRoot: toHexString(finalizedCheckpoint.root),
91+
executionPayloadBlockHash: null,
92+
executionStatus: ExecutionStatus.PreMerge,
93+
timeliness: false,
94+
dataAvailabilityStatus: DataAvailabilityStatus.PreData,
95+
},
96+
slot
97+
);
98+
}
10599

106-
const fcStore: IForkChoiceStore = {
107-
currentSlot: originalState.slot,
108-
justified: {
109-
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
110-
balances: originalState.epochCtx.effectiveBalanceIncrements,
111-
totalBalance,
112-
},
113-
unrealizedJustified: {
114-
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
115-
balances: originalState.epochCtx.effectiveBalanceIncrements,
116-
},
117-
finalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
118-
unrealizedFinalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
119-
justifiedBalancesGetter: () => originalState.epochCtx.effectiveBalanceIncrements,
120-
equivocatingIndices: new Set(),
121-
};
122-
forkchoice = new ForkChoice(originalState.config, fcStore, protoArray);
123-
});
100+
let totalBalance = 0;
101+
for (let i = 0; i < originalState.epochCtx.effectiveBalanceIncrements.length; i++) {
102+
totalBalance += originalState.epochCtx.effectiveBalanceIncrements[i];
103+
}
104+
105+
const fcStore: IForkChoiceStore = {
106+
currentSlot: originalState.slot,
107+
justified: {
108+
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
109+
balances: originalState.epochCtx.effectiveBalanceIncrements,
110+
totalBalance,
111+
},
112+
unrealizedJustified: {
113+
checkpoint: {...justifiedCheckpoint, rootHex: toHexString(justifiedCheckpoint.root)},
114+
balances: originalState.epochCtx.effectiveBalanceIncrements,
115+
},
116+
finalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
117+
unrealizedFinalizedCheckpoint: {...finalizedCheckpoint, rootHex: toHexString(finalizedCheckpoint.root)},
118+
justifiedBalancesGetter: () => originalState.epochCtx.effectiveBalanceIncrements,
119+
equivocatingIndices: new Set(),
120+
};
121+
forkchoice = new ForkChoice(originalState.config, fcStore, protoArray);
122+
},
123+
5 * 60 * 1000
124+
);
124125

125126
// notSeenSlots should be >=1
126127
for (const [notSeenSlots, numMissedVotes, numBadVotes] of [
@@ -129,7 +130,7 @@ describe(`getAttestationsForBlock vc=${vc}`, () => {
129130
// notSeenSlots=2 means the previous block slot is missed
130131
[2, 1, 10],
131132
]) {
132-
itBench({
133+
bench({
133134
id: `notSeenSlots=${notSeenSlots} numMissedVotes=${numMissedVotes} numBadVotes=${numBadVotes}`,
134135
before: () => {
135136
const state = originalState.clone();
@@ -181,7 +182,7 @@ describe.skip("getAttestationsForBlock aggregationBits intersectValues vs get",
181182
const aggregationBits = BitArray.fromBoolArray(Array.from({length: committeeLen}, () => true));
182183
const notSeenValidatorIndices = Array.from({length: committeeLen}, (_, i) => i);
183184

184-
itBench({
185+
bench({
185186
id: "aggregationBits.intersectValues()",
186187
fn: () => {
187188
for (let i = 0; i < runsFactor; i++) {
@@ -191,7 +192,7 @@ describe.skip("getAttestationsForBlock aggregationBits intersectValues vs get",
191192
runsFactor,
192193
});
193194

194-
itBench({
195+
bench({
195196
id: "aggregationBits.get()",
196197
fn: () => {
197198
for (let i = 0; i < runsFactor; i++) {
@@ -203,7 +204,7 @@ describe.skip("getAttestationsForBlock aggregationBits intersectValues vs get",
203204
runsFactor,
204205
});
205206

206-
itBench({
207+
bench({
207208
id: "aggregationBits.get() with push()",
208209
fn: () => {
209210
for (let i = 0; i < runsFactor; i++) {

0 commit comments

Comments
 (0)