Skip to content

Commit

Permalink
accounts-db: Sampled LRU eviction (solana-labs#3943)
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 8 and it's used as the default one.

The new eviction mechanism results in performance improvement in the
cache eviction benchmarks:

```
read_only_accounts_cache_eviction_lo_hi/load/8
                        time:   [1.3057 µs 1.3065 µs 1.3076 µs]
                        change: [-63.644% -60.958% -57.959%] (p = 0.00 < 0.05)
                        Performance has improved.
read_only_accounts_cache_eviction_lo_hi/store/8
                        time:   [2.1751 µs 2.1809 µs 2.1856 µs]
                        change: [-79.874% -79.642% -79.420%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) low mild
read_only_accounts_cache_eviction_lo_hi/load/16
                        time:   [2.3593 µs 2.3614 µs 2.3630 µs]
                        change: [-56.035% -52.626% -48.887%] (p = 0.00 < 0.05)
                        Performance has improved.
read_only_accounts_cache_eviction_lo_hi/store/16
                        time:   [3.1320 µs 3.1436 µs 3.1558 µs]
                        change: [-85.141% -84.976% -84.813%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 13 outliers among 100 measurements (13.00%)
  7 (7.00%) low severe
  5 (5.00%) low mild
  1 (1.00%) high severe
read_only_accounts_cache_eviction_lo_hi/load/32
                        time:   [1.5572 µs 1.5614 µs 1.5662 µs]
                        change: [-85.886% -84.973% -83.975%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  8 (8.00%) high mild
  1 (1.00%) high severe
read_only_accounts_cache_eviction_lo_hi/store/32
                        time:   [3.3965 µs 3.4077 µs 3.4184 µs]
                        change: [-90.858% -90.724% -90.585%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) low severe
  2 (2.00%) low mild
read_only_accounts_cache_eviction_lo_hi/load/64
                        time:   [2.2669 µs 2.5165 µs 2.7736 µs]
                        change: [-91.867% -91.083% -90.189%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild
read_only_accounts_cache_eviction_lo_hi/store/64
                        time:   [6.6097 µs 6.9530 µs 7.2983 µs]
                        change: [-91.090% -90.788% -90.417%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 26 outliers among 100 measurements (26.00%)
  3 (3.00%) low severe
  7 (7.00%) low mild
  6 (6.00%) high mild
  10 (10.00%) high severe

read_only_accounts_cache_eviction_hi/load/8
                        time:   [2.1554 µs 2.1580 µs 2.1600 µs]
                        change: [-44.674% -39.965% -34.699%] (p = 0.00 < 0.05)
                        Performance has improved.
read_only_accounts_cache_eviction_hi/store/8
                        time:   [2.7438 µs 2.7580 µs 2.7689 µs]
                        change: [-80.454% -80.144% -79.805%] (p = 0.00 < 0.05)
                        Performance has improved.
read_only_accounts_cache_eviction_hi/load/16
                        time:   [1.9593 µs 1.9604 µs 1.9619 µs]
                        change: [-64.954% -61.744% -58.367%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild
read_only_accounts_cache_eviction_hi/store/16
                        time:   [2.7436 µs 2.7581 µs 2.7697 µs]
                        change: [-84.959% -84.721% -84.489%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) low mild
read_only_accounts_cache_eviction_hi/load/32
                        time:   [2.5226 µs 2.5260 µs 2.5301 µs]
                        change: [-79.214% -77.822% -76.252%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  8 (8.00%) low mild
read_only_accounts_cache_eviction_hi/store/32
                        time:   [4.0885 µs 4.1015 µs 4.1148 µs]
                        change: [-89.677% -89.496% -89.310%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  7 (7.00%) low severe
  4 (4.00%) low mild
read_only_accounts_cache_eviction_hi/load/64
                        time:   [4.1474 µs 4.3543 µs 4.5721 µs]
                        change: [-86.307% -85.336% -84.364%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) low mild
read_only_accounts_cache_eviction_hi/store/64
                        time:   [5.8232 µs 6.3214 µs 6.7945 µs]
                        change: [-93.624% -93.167% -92.638%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild
```
  • Loading branch information
vadorovsky authored Jan 16, 2025
1 parent 8f9d3d4 commit 1b99961
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 226 deletions.
4 changes: 2 additions & 2 deletions accounts-db/benches/read_only_accounts_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,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::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
AccountsDb::DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE,
));

for (pubkey, account) in accounts.iter() {
Expand Down Expand Up @@ -180,7 +180,7 @@ fn bench_read_only_accounts_cache_eviction(
let cache = Arc::new(ReadOnlyAccountsCache::new(
max_data_size_lo,
max_data_size_hi,
AccountsDb::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
AccountsDb::DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE,
));

// Fill up the cache.
Expand Down
18 changes: 13 additions & 5 deletions accounts-db/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,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 Down Expand Up @@ -529,6 +530,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 @@ -654,6 +656,9 @@ 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)>,
/// The number of elements that will be randomly sampled at eviction time,
/// the oldest of which will get evicted.
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,17 +1896,17 @@ pub struct PubkeyHashAccount {
impl AccountsDb {
pub const DEFAULT_ACCOUNTS_HASH_CACHE_DIR: &'static str = "accounts_hash_cache";

// read only cache does not update lru on read of an entry unless it has been at least this many ms since the last lru update
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE: u32 = 100;

// The default high and low watermark sizes for the accounts read cache.
// If the cache size exceeds MAX_SIZE_HI, it'll evict entries until the size is <= MAX_SIZE_LO.
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_LO: usize = 400 * 1024 * 1024;
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_HI: usize = 410 * 1024 * 1024;

// See AccountsDbConfig::read_cache_evict_sample_size.
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
const DEFAULT_READ_ONLY_CACHE_EVICT_SAMPLE_SIZE: usize = 8;

pub fn default_for_tests() -> Self {
Self::new_single_for_tests()
}
Expand Down Expand Up @@ -1979,6 +1984,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 @@ -2034,7 +2042,7 @@ impl AccountsDb {
read_only_accounts_cache: ReadOnlyAccountsCache::new(
read_cache_size.0,
read_cache_size.1,
Self::READ_ONLY_CACHE_MS_TO_SKIP_LRU_UPDATE,
read_cache_evict_sample_size,
),
write_cache_limit_bytes: accounts_db_config.write_cache_limit_bytes,
partitioned_epoch_rewards_config: accounts_db_config.partitioned_epoch_rewards_config,
Expand Down
Loading

0 comments on commit 1b99961

Please sign in to comment.