-
-
Notifications
You must be signed in to change notification settings - Fork 479
perf(types/electra): model Pending* electra types as ContainerNodeStructType #9054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
packages/state-transition/test/perf/epoch/processPendingDeposits.test.ts
This file contains hidden or 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,83 @@ | ||
| import {bench, describe} from "@chainsafe/benchmark"; | ||
| import {ContainerNodeStructType, ContainerType, ListCompositeType} from "@chainsafe/ssz"; | ||
| import {PENDING_DEPOSITS_LIMIT} from "@lodestar/params"; | ||
| import {ssz} from "@lodestar/types"; | ||
|
|
||
| // PERF: Cost is O(pendingDeposits.length). In the worst case (large deposit queue after a big network event), | ||
| // this can be 50_000 items. Each item requires reading all 5 fields. | ||
| // | ||
| // Benchmarks ContainerType (current) vs ContainerNodeStructType (proposed) for field access performance. | ||
| // ContainerNodeStructType stores items as plain JS objects, avoiding tree traversal on every field read. | ||
|
|
||
| const NUM_DEPOSITS = 50_000; | ||
| const CHUNK = 100; | ||
|
|
||
| // Reuse the same field types as the existing PendingDeposit SSZ type | ||
| const fields = ssz.electra.PendingDeposit.fields; | ||
|
|
||
| const PendingDepositContainer = new ContainerType(fields, {typeName: "PendingDeposit", jsonCase: "eth2"}); | ||
| const PendingDepositNodeStruct = new ContainerNodeStructType(fields, {typeName: "PendingDeposit", jsonCase: "eth2"}); | ||
|
|
||
| const ListContainer = new ListCompositeType(PendingDepositContainer, PENDING_DEPOSITS_LIMIT); | ||
| const ListNodeStruct = new ListCompositeType(PendingDepositNodeStruct, PENDING_DEPOSITS_LIMIT); | ||
|
|
||
| function buildList(listType: typeof ListContainer): ReturnType<typeof ListContainer.defaultViewDU>; | ||
| function buildList(listType: typeof ListNodeStruct): ReturnType<typeof ListNodeStruct.defaultViewDU>; | ||
| function buildList(listType: typeof ListContainer | typeof ListNodeStruct) { | ||
| const view = listType.defaultViewDU(); | ||
| const defaultDeposit = ssz.electra.PendingDeposit.defaultValue(); | ||
| for (let i = 0; i < NUM_DEPOSITS; i++) { | ||
| if (listType === ListContainer) { | ||
| view.push(PendingDepositContainer.toViewDU(defaultDeposit)); | ||
| } else { | ||
| view.push(PendingDepositNodeStruct.toViewDU(defaultDeposit)); | ||
| } | ||
| } | ||
| view.commit(); | ||
| return view; | ||
| } | ||
|
|
||
| describe.skip(`processPendingDeposits - iterate ${NUM_DEPOSITS} deposits, access all fields`, () => { | ||
| const containerListView = buildList(ListContainer); | ||
| const nodeStructListView = buildList(ListNodeStruct); | ||
|
|
||
| bench({ | ||
| id: `ContainerType - getReadonlyByRange chunk=${CHUNK}`, | ||
| yieldEventLoopAfterEach: true, | ||
| fn: () => { | ||
| let sum = 0; | ||
| for (let i = 0; i < NUM_DEPOSITS; i += CHUNK) { | ||
| const deposits = containerListView.getReadonlyByRange(i, CHUNK); | ||
| for (const deposit of deposits) { | ||
| sum += deposit.amount + deposit.slot; | ||
| void deposit.pubkey; | ||
| void deposit.withdrawalCredentials; | ||
| void deposit.signature; | ||
| } | ||
| } | ||
| if (sum === Number.MIN_SAFE_INTEGER) { | ||
| throw new Error("unreachable"); | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| bench({ | ||
| id: `ContainerNodeStructType - getReadonlyByRange chunk=${CHUNK}`, | ||
| yieldEventLoopAfterEach: true, | ||
| fn: () => { | ||
| let sum = 0; | ||
| for (let i = 0; i < NUM_DEPOSITS; i += CHUNK) { | ||
| const deposits = nodeStructListView.getReadonlyByRange(i, CHUNK); | ||
| for (const deposit of deposits) { | ||
| sum += deposit.amount + deposit.slot; | ||
| void deposit.pubkey; | ||
| void deposit.withdrawalCredentials; | ||
| void deposit.signature; | ||
| } | ||
| } | ||
| if (sum === Number.MIN_SAFE_INTEGER) { | ||
| throw new Error("unreachable"); | ||
| } | ||
| }, | ||
| }); | ||
| }); | ||
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
buildListfunction can be simplified by hoisting the type check out of the loop. Instead of checkinglistTypeon every iteration, you can get theelementTypefrom the list type once before the loop. This makes the code cleaner and slightly more performant.