-
-
Notifications
You must be signed in to change notification settings - Fork 299
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
feat: implement BufferPool for PersistentCPStateCache #6269
feat: implement BufferPool for PersistentCPStateCache #6269
Conversation
this.stateCache.add(postState); | ||
this.checkpointStateCache.processState(blockRootHex, postState).catch((e) => { |
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 current CheckpointStateCache
implementation does nothing on this call
/** | ||
* Prune or persist checkpoint states in an epoch, see the description in `processState()` function | ||
*/ | ||
private async processPastEpoch( |
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.
nothing new for this function, it's moved from processState()
to make that function shorter
Performance Report✔️ no performance regression detected 🚀🚀 Significant benchmark improvement detected
Full benchmark results
|
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## unstable #6269 +/- ##
============================================
- Coverage 80.38% 76.53% -3.86%
============================================
Files 202 248 +46
Lines 19622 25943 +6321
Branches 1176 1449 +273
============================================
+ Hits 15773 19855 +4082
- Misses 3821 6058 +2237
- Partials 28 30 +2 |
const CHECKPOINT_FILE_NAME_LENGTH = 82; | ||
|
||
/** | ||
* Implementation of CPStatePersistentApis using file system, this is beneficial for debugging. |
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.
* Implementation of CPStatePersistentApis using file system, this is beneficial for debugging. | |
* Implementation of CPStateDatastore using file system, this is beneficial for debugging. |
let bufferPoolKey: number | undefined = undefined; | ||
try { | ||
const timer = this.metrics?.statePersistDuration.startTimer(); | ||
const stateBytesWithKey = this.serializeState(state); | ||
bufferPoolKey = stateBytesWithKey.key; | ||
persistedKey = await this.datastore.write(cpPersist, stateBytesWithKey.data); | ||
timer?.(); | ||
} finally { | ||
if (bufferPoolKey !== undefined) { | ||
this.bufferPool?.free(bufferPoolKey); | ||
} | ||
} |
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.
this pattern seems like the ideal usecase for the using
pattern from
- https://github.com/tc39/proposal-explicit-resource-management
- https://medium.com/@bagherani/ecmascript-explicit-resource-management-early-implementation-in-typescript-5-2-5e4d08b2aee3
let bufferPoolKey: number | undefined = undefined; | |
try { | |
const timer = this.metrics?.statePersistDuration.startTimer(); | |
const stateBytesWithKey = this.serializeState(state); | |
bufferPoolKey = stateBytesWithKey.key; | |
persistedKey = await this.datastore.write(cpPersist, stateBytesWithKey.data); | |
timer?.(); | |
} finally { | |
if (bufferPoolKey !== undefined) { | |
this.bufferPool?.free(bufferPoolKey); | |
} | |
} | |
{ | |
const timer = this.metrics?.statePersistDuration.startTimer(); | |
using stateBytesWithKey = this.serializeState(state); | |
bufferPoolKey = stateBytesWithKey.key; | |
persistedKey = await this.datastore.write(cpPersist, stateBytesWithKey.data); | |
timer?.(); | |
} |
I'm not sure that we should use this feature, but it's worth knowing about.
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.
@wemeetagain thanks it's a nice feature and really suitable for this code place, implemented it 👍
/** | ||
* Marks the buffer as free. | ||
*/ | ||
free(key: number): void { |
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.
I was wondering why we need to track a currentKey
at all? But I guess we need to handle cases where two calls to getOrReload
can happen at the same time?
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.
yes at least now we call it in 2 flows: to serialize state at every epoch and to serialize validators to reload state. This is mainly to avoid memory allocation, plus we may have an optimized way to serialize validators as in serializeState.test.ts
// for test only | ||
processLateBlock?: boolean; |
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.
// for test only | |
processLateBlock?: boolean; | |
/** for testing only */ | |
processLateBlock?: boolean; |
import {CheckpointHex, CacheItemType, CheckpointStateCache} from "./types.js"; | ||
|
||
export type PersistentCheckpointStateCacheOpts = { | ||
// Keep max n states in memory, persist the rest to disk |
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.
// Keep max n states in memory, persist the rest to disk | |
/** Keep max n states in memory, persist the rest to disk */ |
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.
lgtm
thanks @nazarhussain for the vitest fix ❤️ |
* feat: implement BufferPool for PersistentCPStateCache * fix: alloc vs allocUnsafe for BufferPool * chore: conform to style guide * feat: use using with Disposable object * Add custom build target for beacon-node unit tests * chore: address PR comments --------- Co-authored-by: Nazar Hussain <nazarhussain@gmail.com>
🎉 This PR is included in v1.15.0 🎉 |
Motivation
serializeState.test.ts
benchmark, memory allocation cost is too much while we have memory allocation demand to serialize the whole state at every epochDescription
GROW_RATIO
to alloc more memory once when we reach limitationfs
for debugging purpose, will add a new flag to configure it in the next PRpart of #5968