Skip to content

Commit

Permalink
accounts-db: Sampled LRU eviction
Browse files Browse the repository at this point in the history
Instead of rigid LRU eviction, perform sampled LRU eviction.

Sampled LRU eviction takes K random keys and picks the one with the
lowest update timestamp. This way, even though the evicted element is
not always the oldest in the whole cache, removes the necessity of
maintaining a queue and reduces the contention caused by locking the
queue.

The K parameter is configurable, but the best performing value so far
was 16 and it's used as the default one.

The already existing `concurrent_{read,scan}_write` benchmarks are not
sufficient for benchmarking the eviction and evaluating what kind of
eviction policy performs the best, because they don't fill up the cache,
so eviction never happens.

To address that, add a new benchmark group which measures time spent on
concurrent writes and reads on a full cache (where each write causes
eviction). To reduce the noise coming from other parts of accounts-db,
use only the cache in that benchmark.

The result of that benchmark with the old rigid LRU evction mechamism,
for 128 read and write threads, is:

```
cache_eviction/cache_eviction/128
                        time:   [20.737 s 20.936 s 21.138 s]
```

With sampled LRU eviction (K=16), it improves to:

```
cache_eviction/cache_eviction_k_16/128
                        time:   [7.6609 s 7.7177 s 7.7847 s]
```
  • Loading branch information
vadorovsky committed Dec 26, 2024
1 parent e063c19 commit e8597e4
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 81 deletions.
2 changes: 2 additions & 0 deletions accounts-db/benches/read_only_accounts_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ fn bench_read_only_accounts_cache(c: &mut Criterion) {
let cache = Arc::new(ReadOnlyAccountsCache::new(
AccountsDb::DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_LO,
AccountsDb::DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_HI,
AccountsDb::DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE,
AccountsDb::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
));

Expand Down Expand Up @@ -181,6 +182,7 @@ fn bench_read_only_accounts_cache_eviction(
let cache = Arc::new(ReadOnlyAccountsCache::new(
max_data_size_lo,
max_data_size_hi,
AccountsDb::DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE,
AccountsDb::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
));

Expand Down
11 changes: 11 additions & 0 deletions accounts-db/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ pub const ACCOUNTS_DB_CONFIG_FOR_TESTING: AccountsDbConfig = AccountsDbConfig {
shrink_paths: None,
shrink_ratio: DEFAULT_ACCOUNTS_SHRINK_THRESHOLD_OPTION,
read_cache_limit_bytes: None,
read_cache_evict_sample_size: None,
write_cache_limit_bytes: None,
ancient_append_vec_offset: None,
ancient_storage_ideal_size: None,
Expand All @@ -525,6 +526,7 @@ pub const ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS: AccountsDbConfig = AccountsDbConfig
shrink_paths: None,
shrink_ratio: DEFAULT_ACCOUNTS_SHRINK_THRESHOLD_OPTION,
read_cache_limit_bytes: None,
read_cache_evict_sample_size: None,
write_cache_limit_bytes: None,
ancient_append_vec_offset: None,
ancient_storage_ideal_size: None,
Expand Down Expand Up @@ -649,6 +651,7 @@ pub struct AccountsDbConfig {
/// The low and high watermark sizes for the read cache, in bytes.
/// If None, defaults will be used.
pub read_cache_limit_bytes: Option<(usize, usize)>,
pub read_cache_evict_sample_size: Option<usize>,
pub write_cache_limit_bytes: Option<u64>,
/// if None, ancient append vecs are set to ANCIENT_APPEND_VEC_DEFAULT_OFFSET
/// Some(offset) means include slots up to (max_slot - (slots_per_epoch - 'offset'))
Expand Down Expand Up @@ -1891,6 +1894,10 @@ impl AccountsDb {
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_HI: usize = 410 * 1024 * 1024;

/// Size of the sample for the cache eviction.
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE: usize = 16;

pub fn default_for_tests() -> Self {
Self::new_single_for_tests()
}
Expand Down Expand Up @@ -1972,6 +1979,9 @@ impl AccountsDb {
Self::DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_LO,
Self::DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_HI,
));
let read_cache_evict_sample_size = accounts_db_config
.read_cache_evict_sample_size
.unwrap_or(Self::DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE);

// Increase the stack for foreground threads
// rayon needs a lot of stack
Expand Down Expand Up @@ -2027,6 +2037,7 @@ impl AccountsDb {
read_only_accounts_cache: ReadOnlyAccountsCache::new(
read_cache_size.0,
read_cache_size.1,
read_cache_evict_sample_size,
Self::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
),
write_cache_limit_bytes: accounts_db_config.write_cache_limit_bytes,
Expand Down
Loading

0 comments on commit e8597e4

Please sign in to comment.