-
-
Notifications
You must be signed in to change notification settings - Fork 291
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
Clone states only when necessary #4279
Changes from all commits
309c973
5347c3c
023ae85
c08477e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,21 +41,12 @@ export class StateContextCache { | |
} | ||
|
||
this.metrics?.hits.inc(); | ||
// clonedCount + 1 as there's a .clone() below | ||
this.metrics?.stateClonedCount.observe(item.clonedCount + 1); | ||
if (!stateInternalCachePopulated(item)) { | ||
this.metrics?.stateInternalCacheMiss.inc(); | ||
} | ||
|
||
// Clone first to account for metrics below | ||
const itemCloned = item.clone(); | ||
|
||
this.metrics?.stateClonedCount.observe(item.clonedCount); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we don't call |
||
if (!stateInternalCachePopulated(item)) { | ||
this.metrics?.stateInternalCacheMiss.inc(); | ||
} | ||
|
||
return itemCloned; | ||
return item; | ||
} | ||
|
||
add(item: CachedBeaconStateAllForks): void { | ||
|
@@ -64,7 +55,7 @@ export class StateContextCache { | |
return; | ||
} | ||
this.metrics?.adds.inc(); | ||
this.cache.set(key, item.clone()); | ||
this.cache.set(key, item); | ||
const epoch = item.epochCtx.epoch; | ||
const blockRoots = this.epochIndex.get(epoch); | ||
if (blockRoots) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import {FAR_FUTURE_EPOCH} from "@lodestar/params"; | ||
import {phase0} from "@lodestar/types"; | ||
import {CompositeViewDU} from "@chainsafe/ssz"; | ||
import {ssz} from "@lodestar/types"; | ||
import {CachedBeaconStateAllForks} from "../types.js"; | ||
|
||
/** | ||
|
@@ -22,7 +23,10 @@ import {CachedBeaconStateAllForks} from "../types.js"; | |
* ``` | ||
* Forcing consumers to pass the SubTree of `validator` directly mitigates this issue. | ||
*/ | ||
export function initiateValidatorExit(state: CachedBeaconStateAllForks, validator: phase0.Validator): void { | ||
export function initiateValidatorExit( | ||
state: CachedBeaconStateAllForks, | ||
validator: CompositeViewDU<typeof ssz.phase0.Validator> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
): void { | ||
const {config, epochCtx} = state; | ||
|
||
// return if validator already initiated exit | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ export function stateTransition( | |
const block = signedBlock.message; | ||
const blockSlot = block.slot; | ||
|
||
// .clone() before mutating state in state transition | ||
let postState = state.clone(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and line 85 are the only two instances where state is cloned |
||
|
||
// State is already a ViewDU, which won't commit changes. Equivalent to .setStateCachesAsTransient() | ||
|
@@ -88,6 +89,7 @@ export function processSlots( | |
epochProcessOpts?: EpochProcessOpts, | ||
metrics?: IBeaconStateTransitionMetrics | null | ||
): CachedBeaconStateAllForks { | ||
// .clone() before mutating state in state transition | ||
let postState = state.clone(); | ||
|
||
// State is already a ViewDU, which won't commit changes. Equivalent to .setStateCachesAsTransient() | ||
|
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.
@dapplion I think it's safer to do
postState.clone(false)
?from your implementation
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.
Sorry I don't follow, what would it be safer to do a clone here?
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.
@tuyennhv The point of this PR is to think critically about when our state can actually be mutated. My opinion is that we clone is too many places that a state can't be mutated so cloning is an unnecessary expense that slows than Lodestar for no gain
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.
@dapplion in the past we used to have an issue of missing a
clone()
call, it really took time to debug in that case, @wemeetagain may experience itso what I mean is instead of dropping the
clone()
call, can we doclone(false)
with less performance impact?if both
clone()
andclone(false)
cause performance issue then we need to benchmark to improve performance?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.
My confusion: I thought the cache is merely used for the mutation, it also contains the index of nodes so that we only have to traverse to the node once.
if we use
clone(false)
, the consumer does not have a chance to use the cachednodes
of the tree so it's all bad going with eitherclone() or clone(false)
with ssz v2, we should not ever have a state mutation unless in state transition, thanks @dapplion for your explanation 👍