mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-30 03:14:25 +00:00
Use hashlink over lru for LruCache (#8911)
Use the `LruCache` implementation provided by `hashlink` instead of the current `lru` one. This is mostly a 1-to-1 swap with only slight API incompatibilities. I have decided to leave some config files which previously used `NonZeroUsize` but they may not be required anymore and could potentially switch to `usize`. Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
@@ -16,10 +16,10 @@ directory = { workspace = true }
|
||||
ethereum_ssz = { workspace = true }
|
||||
ethereum_ssz_derive = { workspace = true }
|
||||
fixed_bytes = { workspace = true }
|
||||
hashlink = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
leveldb = { version = "0.8.6", optional = true, default-features = false }
|
||||
logging = { workspace = true }
|
||||
lru = { workspace = true }
|
||||
metrics = { workspace = true }
|
||||
milhouse = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::hdiff::{Error, HDiffBuffer};
|
||||
use crate::metrics;
|
||||
use lru::LruCache;
|
||||
use std::num::NonZeroUsize;
|
||||
use hashlink::lru_cache::LruCache;
|
||||
use types::{BeaconState, ChainSpec, EthSpec, Slot};
|
||||
|
||||
/// Holds a combination of finalized states in two formats:
|
||||
@@ -25,7 +24,7 @@ pub struct Metrics {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> HistoricStateCache<E> {
|
||||
pub fn new(hdiff_buffer_cache_size: NonZeroUsize, state_cache_size: NonZeroUsize) -> Self {
|
||||
pub fn new(hdiff_buffer_cache_size: usize, state_cache_size: usize) -> Self {
|
||||
Self {
|
||||
hdiff_buffers: LruCache::new(hdiff_buffer_cache_size),
|
||||
states: LruCache::new(state_cache_size),
|
||||
@@ -47,7 +46,7 @@ impl<E: EthSpec> HistoricStateCache<E> {
|
||||
);
|
||||
let cloned = buffer.clone();
|
||||
drop(_timer);
|
||||
self.hdiff_buffers.put(slot, cloned);
|
||||
self.hdiff_buffers.insert(slot, cloned);
|
||||
Some(buffer)
|
||||
} else {
|
||||
None
|
||||
@@ -63,7 +62,7 @@ impl<E: EthSpec> HistoricStateCache<E> {
|
||||
Ok(Some(state.clone()))
|
||||
} else if let Some(buffer) = self.hdiff_buffers.get(&slot) {
|
||||
let state = buffer.as_state(spec)?;
|
||||
self.states.put(slot, state.clone());
|
||||
self.states.insert(slot, state.clone());
|
||||
Ok(Some(state))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -71,11 +70,11 @@ impl<E: EthSpec> HistoricStateCache<E> {
|
||||
}
|
||||
|
||||
pub fn put_state(&mut self, slot: Slot, state: BeaconState<E>) {
|
||||
self.states.put(slot, state);
|
||||
self.states.insert(slot, state);
|
||||
}
|
||||
|
||||
pub fn put_hdiff_buffer(&mut self, slot: Slot, buffer: HDiffBuffer) {
|
||||
self.hdiff_buffers.put(slot, buffer);
|
||||
self.hdiff_buffers.insert(slot, buffer);
|
||||
}
|
||||
|
||||
pub fn put_both(&mut self, slot: Slot, state: BeaconState<E>, buffer: HDiffBuffer) {
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::{
|
||||
parse_data_column_key,
|
||||
};
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use hashlink::lru_cache::LruCache;
|
||||
use itertools::{Itertools, process_results};
|
||||
use lru::LruCache;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use safe_arith::SafeArith;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -34,7 +34,6 @@ use std::cmp::{Ordering, min};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io::{Read, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -97,7 +96,7 @@ struct BlockCache<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BlockCache<E> {
|
||||
pub fn new(size: NonZeroUsize) -> Self {
|
||||
pub fn new(size: usize) -> Self {
|
||||
Self {
|
||||
block_cache: LruCache::new(size),
|
||||
blob_cache: LruCache::new(size),
|
||||
@@ -106,14 +105,15 @@ impl<E: EthSpec> BlockCache<E> {
|
||||
}
|
||||
}
|
||||
pub fn put_block(&mut self, block_root: Hash256, block: SignedBeaconBlock<E>) {
|
||||
self.block_cache.put(block_root, block);
|
||||
self.block_cache.insert(block_root, block);
|
||||
}
|
||||
pub fn put_blobs(&mut self, block_root: Hash256, blobs: BlobSidecarList<E>) {
|
||||
self.blob_cache.put(block_root, blobs);
|
||||
self.blob_cache.insert(block_root, blobs);
|
||||
}
|
||||
pub fn put_data_column(&mut self, block_root: Hash256, data_column: Arc<DataColumnSidecar<E>>) {
|
||||
self.data_column_cache
|
||||
.get_or_insert_mut(block_root, Default::default)
|
||||
.entry(block_root)
|
||||
.or_insert_with(Default::default)
|
||||
.insert(*data_column.index(), data_column);
|
||||
}
|
||||
pub fn put_data_column_custody_info(
|
||||
@@ -143,13 +143,13 @@ impl<E: EthSpec> BlockCache<E> {
|
||||
self.data_column_custody_info_cache.clone()
|
||||
}
|
||||
pub fn delete_block(&mut self, block_root: &Hash256) {
|
||||
let _ = self.block_cache.pop(block_root);
|
||||
let _ = self.block_cache.remove(block_root);
|
||||
}
|
||||
pub fn delete_blobs(&mut self, block_root: &Hash256) {
|
||||
let _ = self.blob_cache.pop(block_root);
|
||||
let _ = self.blob_cache.remove(block_root);
|
||||
}
|
||||
pub fn delete_data_columns(&mut self, block_root: &Hash256) {
|
||||
let _ = self.data_column_cache.pop(block_root);
|
||||
let _ = self.data_column_cache.remove(block_root);
|
||||
}
|
||||
pub fn delete(&mut self, block_root: &Hash256) {
|
||||
self.delete_block(block_root);
|
||||
@@ -236,17 +236,16 @@ impl<E: EthSpec> HotColdDB<E, MemoryStore, MemoryStore> {
|
||||
cold_db: MemoryStore::open(),
|
||||
blobs_db: MemoryStore::open(),
|
||||
hot_db: MemoryStore::open(),
|
||||
block_cache: NonZeroUsize::new(config.block_cache_size)
|
||||
.map(BlockCache::new)
|
||||
.map(Mutex::new),
|
||||
block_cache: (config.block_cache_size > 0)
|
||||
.then(|| Mutex::new(BlockCache::new(config.block_cache_size))),
|
||||
state_cache: Mutex::new(StateCache::new(
|
||||
config.state_cache_size,
|
||||
config.state_cache_headroom,
|
||||
config.hot_hdiff_buffer_cache_size,
|
||||
)),
|
||||
historic_state_cache: Mutex::new(HistoricStateCache::new(
|
||||
config.cold_hdiff_buffer_cache_size,
|
||||
config.historic_state_cache_size,
|
||||
config.cold_hdiff_buffer_cache_size.get(),
|
||||
config.historic_state_cache_size.get(),
|
||||
)),
|
||||
config,
|
||||
hierarchy,
|
||||
@@ -290,17 +289,16 @@ impl<E: EthSpec> HotColdDB<E, BeaconNodeBackend, BeaconNodeBackend> {
|
||||
blobs_db: BeaconNodeBackend::open(&config, blobs_db_path)?,
|
||||
cold_db: BeaconNodeBackend::open(&config, cold_path)?,
|
||||
hot_db,
|
||||
block_cache: NonZeroUsize::new(config.block_cache_size)
|
||||
.map(BlockCache::new)
|
||||
.map(Mutex::new),
|
||||
block_cache: (config.block_cache_size > 0)
|
||||
.then(|| Mutex::new(BlockCache::new(config.block_cache_size))),
|
||||
state_cache: Mutex::new(StateCache::new(
|
||||
config.state_cache_size,
|
||||
config.state_cache_headroom,
|
||||
config.hot_hdiff_buffer_cache_size,
|
||||
)),
|
||||
historic_state_cache: Mutex::new(HistoricStateCache::new(
|
||||
config.cold_hdiff_buffer_cache_size,
|
||||
config.historic_state_cache_size,
|
||||
config.cold_hdiff_buffer_cache_size.get(),
|
||||
config.historic_state_cache_size.get(),
|
||||
)),
|
||||
config,
|
||||
hierarchy,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
Error,
|
||||
metrics::{self, HOT_METRIC},
|
||||
};
|
||||
use lru::LruCache;
|
||||
use hashlink::lru_cache::LruCache;
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::num::NonZeroUsize;
|
||||
use tracing::instrument;
|
||||
@@ -86,9 +86,9 @@ impl<E: EthSpec> StateCache<E> {
|
||||
) -> Self {
|
||||
StateCache {
|
||||
finalized_state: None,
|
||||
states: LruCache::new(state_capacity),
|
||||
states: LruCache::new(state_capacity.get()),
|
||||
block_map: BlockMap::default(),
|
||||
hdiff_buffers: HotHDiffBufferCache::new(hdiff_capacity),
|
||||
hdiff_buffers: HotHDiffBufferCache::new(hdiff_capacity.get()),
|
||||
max_epoch: Epoch::new(0),
|
||||
head_block_root: Hash256::ZERO,
|
||||
headroom,
|
||||
@@ -100,7 +100,7 @@ impl<E: EthSpec> StateCache<E> {
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.states.cap().get()
|
||||
self.states.capacity()
|
||||
}
|
||||
|
||||
pub fn num_hdiff_buffers(&self) -> usize {
|
||||
@@ -154,7 +154,7 @@ impl<E: EthSpec> StateCache<E> {
|
||||
// preferences older slots.
|
||||
// NOTE: This isn't perfect as it prunes by slot: there could be multiple buffers
|
||||
// at some slots in the case of long forks without finality.
|
||||
let new_hdiff_cache = HotHDiffBufferCache::new(self.hdiff_buffers.cap());
|
||||
let new_hdiff_cache = HotHDiffBufferCache::new(self.hdiff_buffers.capacity());
|
||||
let old_hdiff_cache = std::mem::replace(&mut self.hdiff_buffers, new_hdiff_cache);
|
||||
for (state_root, (slot, buffer)) in old_hdiff_cache.hdiff_buffers {
|
||||
if pre_finalized_slots_to_retain.contains(&slot) {
|
||||
@@ -164,7 +164,7 @@ impl<E: EthSpec> StateCache<E> {
|
||||
|
||||
// Delete states.
|
||||
for state_root in state_roots_to_prune {
|
||||
if let Some((_, state)) = self.states.pop(&state_root) {
|
||||
if let Some((_, state)) = self.states.remove(&state_root) {
|
||||
// Add the hdiff buffer for this state to the hdiff cache if it is now part of
|
||||
// the pre-finalized grid. The `put` method will take care of keeping the most
|
||||
// useful buffers.
|
||||
@@ -260,7 +260,7 @@ impl<E: EthSpec> StateCache<E> {
|
||||
|
||||
// Insert the full state into the cache.
|
||||
if let Some((deleted_state_root, _)) =
|
||||
self.states.put(state_root, (state_root, state.clone()))
|
||||
self.states.insert(state_root, (state_root, state.clone()))
|
||||
{
|
||||
deleted_states.push(deleted_state_root);
|
||||
}
|
||||
@@ -334,14 +334,14 @@ impl<E: EthSpec> StateCache<E> {
|
||||
}
|
||||
|
||||
pub fn delete_state(&mut self, state_root: &Hash256) {
|
||||
self.states.pop(state_root);
|
||||
self.states.remove(state_root);
|
||||
self.block_map.delete(state_root);
|
||||
}
|
||||
|
||||
pub fn delete_block_states(&mut self, block_root: &Hash256) {
|
||||
if let Some(slot_map) = self.block_map.delete_block_states(block_root) {
|
||||
for state_root in slot_map.slots.values() {
|
||||
self.states.pop(state_root);
|
||||
self.states.remove(state_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,9 +366,10 @@ impl<E: EthSpec> StateCache<E> {
|
||||
let mut old_boundary_state_roots = vec![];
|
||||
let mut good_boundary_state_roots = vec![];
|
||||
|
||||
// Skip the `cull_exempt` most-recently used, then reverse the iterator to start at
|
||||
// least-recently used states.
|
||||
for (&state_root, (_, state)) in self.states.iter().skip(cull_exempt).rev() {
|
||||
// Start at the least-recently used states, excluding the `cull_exempt` most-recently
|
||||
// used (which are the final entries of the iterator).
|
||||
let num_cull_candidates = self.states.len().saturating_sub(cull_exempt);
|
||||
for (&state_root, (_, state)) in self.states.iter().take(num_cull_candidates) {
|
||||
let is_advanced = state.slot() > state.latest_block_header().slot;
|
||||
let is_boundary = state.slot() % E::slots_per_epoch() == 0;
|
||||
let could_finalize =
|
||||
@@ -450,7 +451,7 @@ impl BlockMap {
|
||||
}
|
||||
|
||||
impl HotHDiffBufferCache {
|
||||
pub fn new(capacity: NonZeroUsize) -> Self {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
hdiff_buffers: LruCache::new(capacity),
|
||||
}
|
||||
@@ -467,8 +468,8 @@ impl HotHDiffBufferCache {
|
||||
/// If the value was inserted then `true` is returned.
|
||||
pub fn put(&mut self, state_root: Hash256, slot: Slot, buffer: HDiffBuffer) -> bool {
|
||||
// If the cache is not full, simply insert the value.
|
||||
if self.hdiff_buffers.len() != self.hdiff_buffers.cap().get() {
|
||||
self.hdiff_buffers.put(state_root, (slot, buffer));
|
||||
if self.hdiff_buffers.len() != self.hdiff_buffers.capacity() {
|
||||
self.hdiff_buffers.insert(state_root, (slot, buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -484,23 +485,23 @@ impl HotHDiffBufferCache {
|
||||
return false;
|
||||
};
|
||||
|
||||
if self.hdiff_buffers.cap().get() > 1 || slot < min_slot {
|
||||
if self.hdiff_buffers.capacity() > 1 || slot < min_slot {
|
||||
// Remove LRU value. Cache is now at size `cap - 1`.
|
||||
let Some((removed_state_root, (removed_slot, removed_buffer))) =
|
||||
self.hdiff_buffers.pop_lru()
|
||||
self.hdiff_buffers.remove_lru()
|
||||
else {
|
||||
// Unreachable: cache is full so should have at least one entry to pop.
|
||||
return false;
|
||||
};
|
||||
|
||||
// Insert new value. Cache size is now at size `cap`.
|
||||
self.hdiff_buffers.put(state_root, (slot, buffer));
|
||||
self.hdiff_buffers.insert(state_root, (slot, buffer));
|
||||
|
||||
// If the removed value had the min slot and we didn't intend to replace it (cap=1)
|
||||
// then we reinsert it.
|
||||
if removed_slot == min_slot && slot >= min_slot {
|
||||
self.hdiff_buffers
|
||||
.put(removed_state_root, (removed_slot, removed_buffer));
|
||||
.insert(removed_state_root, (removed_slot, removed_buffer));
|
||||
}
|
||||
true
|
||||
} else {
|
||||
@@ -509,8 +510,8 @@ impl HotHDiffBufferCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cap(&self) -> NonZeroUsize {
|
||||
self.hdiff_buffers.cap()
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.hdiff_buffers.capacity()
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
|
||||
Reference in New Issue
Block a user