Skip to content

Commit a2be7e6

Browse files
removed incremental changes to cached_bytes and added new function to iteratively track memory usage
1 parent fe05ed2 commit a2be7e6

File tree

5 files changed

+76
-27
lines changed

5 files changed

+76
-27
lines changed

beacon_node/beacon_chain/src/chain_config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub const DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION: Epoch = Epoch::new(2);
1010
/// Default to 1/12th of the slot, which is 1 second on mainnet.
1111
pub const DEFAULT_RE_ORG_CUTOFF_DENOMINATOR: u32 = 12;
1212
pub const DEFAULT_FORK_CHOICE_BEFORE_PROPOSAL_TIMEOUT: u64 = 250;
13-
13+
pub const DEFAULT_STATE_CACHE_MAX_BYTES: usize = 536870912;
1414
/// Default fraction of a slot lookahead for payload preparation (12/3 = 4 seconds on mainnet).
1515
pub const DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR: u32 = 3;
1616

beacon_node/store/src/memsize.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use crate::metrics::BEACON_STATE_MEMORY_SIZE_CALCULATION_TIME;
12
use milhouse::mem::{MemorySize, MemoryTracker};
3+
use std::time::Instant;
24
use types::{BeaconState, EthSpec};
35

46
/// BeaconState Wrapper for memory tracking.
5-
struct BeaconStateWrapper<'a, E: EthSpec>(&'a BeaconState<E>);
7+
pub struct BeaconStateWrapper<'a, E: EthSpec>(pub &'a BeaconState<E>);
68

79
impl<'a, E: EthSpec> MemorySize for BeaconStateWrapper<'a, E> {
810
fn self_pointer(&self) -> usize {
@@ -26,10 +28,17 @@ pub trait BeaconStateMemorySize {
2628
impl<E: EthSpec> BeaconStateMemorySize for BeaconState<E> {
2729
fn memory_size(&self) -> usize {
2830
let wrapper = BeaconStateWrapper(self);
29-
31+
// Timer for MemorySize
32+
let timer = Instant::now();
3033
// Use MemoryTracker on the wrapper
3134
let mut tracker = MemoryTracker::default();
3235
let stats = tracker.track_item(&wrapper);
33-
stats.total_size
36+
37+
let elapsed_time = timer.elapsed();
38+
metrics::observe(
39+
&BEACON_STATE_MEMORY_SIZE_CALCULATION_TIME,
40+
elapsed_time.as_secs_f64(),
41+
);
42+
stats.differential_size
3443
}
3544
}

beacon_node/store/src/metrics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@ pub static STORE_BEACON_STATE_CACHE_MEMORY_SIZE: LazyLock<Result<IntGauge>> = La
275275
"Memory consumed by items in the beacon store state cache",
276276
)
277277
});
278+
pub static BEACON_STATE_MEMORY_SIZE_CALCULATION_TIME: LazyLock<Result<Histogram>> =
279+
LazyLock::new(|| {
280+
try_create_histogram(
281+
"beacon_state_memory_size_calculation_time",
282+
"Time taken to calculate the memory size of a beacon state.",
283+
)
284+
});
278285
pub static STORE_BEACON_HISTORIC_STATE_CACHE_SIZE: LazyLock<Result<IntGauge>> =
279286
LazyLock::new(|| {
280287
try_create_int_gauge(

beacon_node/store/src/state_cache.rs

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const CULL_EXEMPT_DENOMINATOR: usize = 10;
1616
/// States that are less than or equal to this many epochs old *could* become finalized and will not
1717
/// be culled from the cache.
1818
const EPOCH_FINALIZATION_LIMIT: u64 = 4;
19-
19+
const RECOMPUTE_INTERVAL: usize = 10;
2020
#[derive(Debug)]
2121
pub struct FinalizedState<E: EthSpec> {
2222
state_root: Hash256,
@@ -48,6 +48,7 @@ pub struct StateCache<E: EthSpec> {
4848
headroom: NonZeroUsize,
4949
cached_bytes: usize,
5050
max_cached_bytes: usize,
51+
put_count: usize,
5152
}
5253

5354
/// Cache of hdiff buffers for hot states.
@@ -97,6 +98,7 @@ impl<E: EthSpec> StateCache<E> {
9798
headroom,
9899
max_cached_bytes,
99100
cached_bytes: 0,
101+
put_count: 0,
100102
}
101103
}
102104

@@ -249,31 +251,30 @@ impl<E: EthSpec> StateCache<E> {
249251
// Update the cache's idea of the max epoch.
250252
self.max_epoch = std::cmp::max(state.current_epoch(), self.max_epoch);
251253

252-
let state_size = state.memory_size();
253-
254-
// If the cache is full or would exceed the byte limit, cull states.
255-
let mut deleted_states = Vec::new();
256-
while self.len() > 0
257-
&& (self.len() >= self.capacity()
258-
|| self.cached_bytes + state_size > self.max_cached_bytes)
259-
{
260-
deleted_states.extend(self.cull(self.headroom.get()));
261-
}
254+
// If the cache is full, use the custom cull routine to make room.
255+
let mut deleted_states =
256+
if let Some(over_capacity) = self.len().checked_sub(self.capacity()) {
257+
// The `over_capacity` should always be 0, but we add it here just in case.
258+
self.cull(over_capacity + self.headroom.get())
259+
} else {
260+
vec![]
261+
};
262262

263263
// Insert the full state into the cache.
264-
if let Some((deleted_state_root, removed_state)) =
264+
if let Some((deleted_state_root, _)) =
265265
self.states.put(state_root, (state_root, state.clone()))
266266
{
267267
deleted_states.push(deleted_state_root);
268-
self.cached_bytes = self
269-
.cached_bytes
270-
.saturating_sub(removed_state.memory_size());
271268
}
272-
273269
// Record the connection from block root and slot to this state.
274270
let slot = state.slot();
275271
self.block_map.insert(block_root, slot, state_root);
276-
self.cached_bytes += state_size;
272+
self.put_count += 1;
273+
274+
if self.put_count >= RECOMPUTE_INTERVAL {
275+
self.recompute_cached_bytes();
276+
self.put_count = 0;
277+
}
277278

278279
Ok(PutStateOutcome::New(deleted_states))
279280
}
@@ -340,11 +341,6 @@ impl<E: EthSpec> StateCache<E> {
340341
}
341342

342343
pub fn delete_state(&mut self, state_root: &Hash256) {
343-
self.cached_bytes = self.cached_bytes.saturating_sub(
344-
self.states
345-
.peek(state_root)
346-
.map_or(0, |(_, state)| state.memory_size()),
347-
);
348344
self.states.pop(state_root);
349345
self.block_map.delete(state_root);
350346
}
@@ -425,6 +421,43 @@ impl<E: EthSpec> StateCache<E> {
425421

426422
state_roots_to_delete
427423
}
424+
425+
fn recompute_cached_bytes(&mut self) {
426+
let mut total_bytes: usize = self
427+
.states
428+
.iter()
429+
.map(|(_, (_, state))| state.memory_size())
430+
.sum();
431+
432+
self.cached_bytes = total_bytes;
433+
434+
// Update the metric
435+
metrics::set_gauge(
436+
&metrics::STORE_BEACON_STATE_CACHE_MEMORY_SIZE,
437+
total_bytes as i64,
438+
);
439+
440+
while total_bytes > self.max_cached_bytes && self.len() > 0 {
441+
// Prune a small number of states
442+
let states_to_prune = std::cmp::min(5, self.len()); // Prune up to 5 states
443+
self.cull(states_to_prune);
444+
445+
// Recompute to see actual memory reduction
446+
total_bytes = self
447+
.states
448+
.iter()
449+
.map(|(_, (_, state))| state.memory_size())
450+
.sum();
451+
452+
self.cached_bytes = total_bytes;
453+
454+
// Update metric again
455+
metrics::set_gauge(
456+
&metrics::STORE_BEACON_STATE_CACHE_MEMORY_SIZE,
457+
total_bytes as i64,
458+
);
459+
}
460+
}
428461
}
429462

430463
impl BlockMap {

lighthouse/tests/beacon_node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1833,7 +1833,7 @@ fn state_cache_max_bytes_default() {
18331833
.run_with_zero_port()
18341834
.with_config(|config| {
18351835
assert_eq!(
1836-
config.store.state_cache_max_bytes,
1836+
config.store.max_state_cache_bytes,
18371837
DEFAULT_STATE_CACHE_MAX_BYTES
18381838
);
18391839
});

0 commit comments

Comments
 (0)