Parallel cache scan (#14544)
* Parallel cache scan * PR comments * PR comments Co-authored-by: Carl Lin <carl@solana.com>
This commit is contained in:
parent
a480b63234
commit
2745b79b74
|
@ -25,15 +25,6 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.3.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
|
||||||
dependencies = [
|
|
||||||
"const-random",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -611,26 +602,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
|
|
||||||
dependencies = [
|
|
||||||
"const-random-macro",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random-macro"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.1.14",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -883,13 +854,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "3.11.10"
|
version = "4.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5"
|
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.3.8",
|
"cfg-if 1.0.0",
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"rayon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -25,15 +25,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.3.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
|
||||||
dependencies = [
|
|
||||||
"const-random",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
|
@ -389,26 +380,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
|
|
||||||
dependencies = [
|
|
||||||
"const-random-macro",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-random-macro"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.1.14",
|
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -604,13 +575,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "3.11.10"
|
version = "4.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5"
|
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"cfg-if 1.0.0",
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"rayon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -14,7 +14,7 @@ blake3 = "0.3.6"
|
||||||
bv = { version = "0.11.1", features = ["serde"] }
|
bv = { version = "0.11.1", features = ["serde"] }
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
bzip2 = "0.3.3"
|
bzip2 = "0.3.3"
|
||||||
dashmap = "3.11.10"
|
dashmap = { version = "4.0.2", features = ["rayon"] }
|
||||||
crossbeam-channel = "0.4"
|
crossbeam-channel = "0.4"
|
||||||
dir-diff = "0.3.2"
|
dir-diff = "0.3.2"
|
||||||
flate2 = "1.0.14"
|
flate2 = "1.0.14"
|
||||||
|
|
|
@ -4,6 +4,7 @@ extern crate test;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use solana_runtime::{
|
use solana_runtime::{
|
||||||
accounts::{create_test_accounts, Accounts},
|
accounts::{create_test_accounts, Accounts},
|
||||||
bank::*,
|
bank::*,
|
||||||
|
@ -11,6 +12,7 @@ use solana_runtime::{
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
genesis_config::{create_genesis_config, ClusterType},
|
genesis_config::{create_genesis_config, ClusterType},
|
||||||
|
hash::Hash,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -297,3 +299,59 @@ fn bench_rwlock_hashmap_single_reader_with_n_writers(bencher: &mut Bencher) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_bench_dashmap_iter() -> (Arc<Accounts>, DashMap<Pubkey, (Account, Hash)>) {
|
||||||
|
let accounts = Arc::new(Accounts::new_with_config(
|
||||||
|
vec![
|
||||||
|
PathBuf::from(std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string()))
|
||||||
|
.join("bench_dashmap_par_iter"),
|
||||||
|
],
|
||||||
|
&ClusterType::Development,
|
||||||
|
HashSet::new(),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
let dashmap = DashMap::new();
|
||||||
|
let num_keys = std::env::var("NUM_BENCH_KEYS")
|
||||||
|
.map(|num_keys| num_keys.parse::<usize>().unwrap())
|
||||||
|
.unwrap_or_else(|_| 10000);
|
||||||
|
for _ in 0..num_keys {
|
||||||
|
dashmap.insert(
|
||||||
|
Pubkey::new_unique(),
|
||||||
|
(
|
||||||
|
Account::new(1, 0, &Account::default().owner),
|
||||||
|
Hash::new_unique(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(accounts, dashmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_dashmap_par_iter(bencher: &mut Bencher) {
|
||||||
|
let (accounts, dashmap) = setup_bench_dashmap_iter();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
test::black_box(accounts.accounts_db.thread_pool.install(|| {
|
||||||
|
dashmap
|
||||||
|
.par_iter()
|
||||||
|
.map(|cached_account| (*cached_account.key(), cached_account.value().1))
|
||||||
|
.collect::<Vec<(Pubkey, Hash)>>()
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_dashmap_iter(bencher: &mut Bencher) {
|
||||||
|
let (_accounts, dashmap) = setup_bench_dashmap_iter();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
test::black_box(
|
||||||
|
dashmap
|
||||||
|
.iter()
|
||||||
|
.map(|cached_account| (*cached_account.key(), cached_account.value().1))
|
||||||
|
.collect::<Vec<(Pubkey, Hash)>>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts_db::{AccountsDB, AppendVecId, BankHashInfo, ErrorCounters, LoadedAccount},
|
accounts_db::{AccountsDB, BankHashInfo, ErrorCounters, LoadedAccount, ScanStorageResult},
|
||||||
accounts_index::{AccountIndex, Ancestors, IndexKey},
|
accounts_index::{AccountIndex, Ancestors, IndexKey},
|
||||||
bank::{
|
bank::{
|
||||||
NonceRollbackFull, NonceRollbackInfo, TransactionCheckResult, TransactionExecutionResult,
|
NonceRollbackFull, NonceRollbackInfo, TransactionCheckResult, TransactionExecutionResult,
|
||||||
|
@ -9,9 +9,12 @@ use crate::{
|
||||||
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
system_instruction_processor::{get_system_account_kind, SystemAccountKind},
|
||||||
transaction_utils::OrderedIterator,
|
transaction_utils::OrderedIterator,
|
||||||
};
|
};
|
||||||
|
use dashmap::{
|
||||||
|
mapref::entry::Entry::{Occupied, Vacant},
|
||||||
|
DashMap,
|
||||||
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use rayon::slice::ParallelSliceMut;
|
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
|
@ -453,28 +456,48 @@ impl Accounts {
|
||||||
pub fn scan_slot<F, B>(&self, slot: Slot, func: F) -> Vec<B>
|
pub fn scan_slot<F, B>(&self, slot: Slot, func: F) -> Vec<B>
|
||||||
where
|
where
|
||||||
F: Fn(LoadedAccount) -> Option<B> + Send + Sync,
|
F: Fn(LoadedAccount) -> Option<B> + Send + Sync,
|
||||||
B: Send + Default,
|
B: Sync + Send + Default + std::cmp::Eq,
|
||||||
{
|
{
|
||||||
let accumulator: Vec<Vec<(Pubkey, u64, B)>> = self.accounts_db.scan_account_storage(
|
let scan_result = self.accounts_db.scan_account_storage(
|
||||||
slot,
|
slot,
|
||||||
|loaded_account: LoadedAccount, _id: AppendVecId, accum: &mut Vec<(Pubkey, u64, B)>| {
|
|loaded_account: LoadedAccount| {
|
||||||
let pubkey = *loaded_account.pubkey();
|
// Cache only has one version per key, don't need to worry about versioning
|
||||||
let write_version = loaded_account.write_version();
|
func(loaded_account)
|
||||||
if let Some(val) = func(loaded_account) {
|
},
|
||||||
accum.push((pubkey, std::u64::MAX - write_version, val));
|
|accum: &DashMap<Pubkey, (u64, B)>, loaded_account: LoadedAccount| {
|
||||||
|
let loaded_account_pubkey = *loaded_account.pubkey();
|
||||||
|
let loaded_write_version = loaded_account.write_version();
|
||||||
|
let should_insert = accum
|
||||||
|
.get(&loaded_account_pubkey)
|
||||||
|
.map(|existing_entry| loaded_write_version > existing_entry.value().0)
|
||||||
|
.unwrap_or(true);
|
||||||
|
if should_insert {
|
||||||
|
if let Some(val) = func(loaded_account) {
|
||||||
|
// Detected insertion is necessary, grabs the write lock to commit the write,
|
||||||
|
match accum.entry(loaded_account_pubkey) {
|
||||||
|
// Double check in case another thread interleaved a write between the read + write.
|
||||||
|
Occupied(mut occupied_entry) => {
|
||||||
|
if loaded_write_version > occupied_entry.get().0 {
|
||||||
|
occupied_entry.insert((loaded_write_version, val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vacant(vacant_entry) => {
|
||||||
|
vacant_entry.insert((loaded_write_version, val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut versions: Vec<(Pubkey, u64, B)> = accumulator.into_iter().flatten().collect();
|
match scan_result {
|
||||||
self.accounts_db.thread_pool.install(|| {
|
ScanStorageResult::Cached(cached_result) => cached_result,
|
||||||
versions.par_sort_by_key(|s| (s.0, s.1));
|
ScanStorageResult::Stored(stored_result) => stored_result
|
||||||
});
|
.into_iter()
|
||||||
versions.dedup_by_key(|s| s.0);
|
.map(|(_pubkey, (_latest_write_version, val))| val)
|
||||||
versions
|
.collect(),
|
||||||
.into_iter()
|
}
|
||||||
.map(|(_pubkey, _version, val)| val)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_by_program_slot(
|
pub fn load_by_program_slot(
|
||||||
|
|
|
@ -27,7 +27,10 @@ use crate::{
|
||||||
contains::Contains,
|
contains::Contains,
|
||||||
};
|
};
|
||||||
use blake3::traits::digest::Digest;
|
use blake3::traits::digest::Digest;
|
||||||
use dashmap::DashMap;
|
use dashmap::{
|
||||||
|
mapref::entry::Entry::{Occupied, Vacant},
|
||||||
|
DashMap, DashSet,
|
||||||
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
||||||
|
@ -62,6 +65,7 @@ const MAX_RECYCLE_STORES: usize = 1000;
|
||||||
const STORE_META_OVERHEAD: usize = 256;
|
const STORE_META_OVERHEAD: usize = 256;
|
||||||
const MAX_CACHE_SLOTS: usize = 200;
|
const MAX_CACHE_SLOTS: usize = 200;
|
||||||
const FLUSH_CACHE_RANDOM_THRESHOLD: usize = MAX_LOCKOUT_HISTORY;
|
const FLUSH_CACHE_RANDOM_THRESHOLD: usize = MAX_LOCKOUT_HISTORY;
|
||||||
|
const SCAN_SLOT_PAR_ITER_THRESHOLD: usize = 4000;
|
||||||
|
|
||||||
pub const DEFAULT_FILE_SIZE: u64 = PAGE_SIZE * 1024;
|
pub const DEFAULT_FILE_SIZE: u64 = PAGE_SIZE * 1024;
|
||||||
pub const DEFAULT_NUM_THREADS: u32 = 8;
|
pub const DEFAULT_NUM_THREADS: u32 = 8;
|
||||||
|
@ -87,12 +91,19 @@ const CACHE_VIRTUAL_WRITE_VERSION: u64 = 0;
|
||||||
const CACHE_VIRTUAL_OFFSET: usize = 0;
|
const CACHE_VIRTUAL_OFFSET: usize = 0;
|
||||||
const CACHE_VIRTUAL_STORED_SIZE: usize = 0;
|
const CACHE_VIRTUAL_STORED_SIZE: usize = 0;
|
||||||
|
|
||||||
|
type DashMapVersionHash = DashMap<Pubkey, (u64, Hash)>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// FROZEN_ACCOUNT_PANIC is used to signal local_cluster that an AccountsDB panic has occurred,
|
// FROZEN_ACCOUNT_PANIC is used to signal local_cluster that an AccountsDB panic has occurred,
|
||||||
// as |cargo test| cannot observe panics in other threads
|
// as |cargo test| cannot observe panics in other threads
|
||||||
pub static ref FROZEN_ACCOUNT_PANIC: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
pub static ref FROZEN_ACCOUNT_PANIC: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ScanStorageResult<R, B> {
|
||||||
|
Cached(Vec<R>),
|
||||||
|
Stored(B),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ErrorCounters {
|
pub struct ErrorCounters {
|
||||||
pub total: usize,
|
pub total: usize,
|
||||||
|
@ -623,7 +634,6 @@ pub struct AccountsDB {
|
||||||
struct AccountsStats {
|
struct AccountsStats {
|
||||||
delta_hash_scan_time_total_us: AtomicU64,
|
delta_hash_scan_time_total_us: AtomicU64,
|
||||||
delta_hash_accumulate_time_total_us: AtomicU64,
|
delta_hash_accumulate_time_total_us: AtomicU64,
|
||||||
delta_hash_merge_time_total_us: AtomicU64,
|
|
||||||
delta_hash_num: AtomicU64,
|
delta_hash_num: AtomicU64,
|
||||||
|
|
||||||
last_store_report: AtomicU64,
|
last_store_report: AtomicU64,
|
||||||
|
@ -1894,35 +1904,46 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scan a specific slot through all the account storage in parallel
|
/// Scan a specific slot through all the account storage in parallel
|
||||||
pub fn scan_account_storage<F, B>(&self, slot: Slot, scan_func: F) -> Vec<B>
|
pub fn scan_account_storage<R, B>(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
cache_map_func: impl Fn(LoadedAccount) -> Option<R> + Sync,
|
||||||
|
storage_scan_func: impl Fn(&B, LoadedAccount) + Sync,
|
||||||
|
) -> ScanStorageResult<R, B>
|
||||||
where
|
where
|
||||||
F: Fn(LoadedAccount, AppendVecId, &mut B) + Send + Sync,
|
R: Send,
|
||||||
B: Send + Default,
|
B: Send + Default + Sync,
|
||||||
{
|
|
||||||
self.scan_account_storage_inner(slot, scan_func)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scan_account_storage_inner<F, B>(&self, slot: Slot, scan_func: F) -> Vec<B>
|
|
||||||
where
|
|
||||||
F: Fn(LoadedAccount, AppendVecId, &mut B) + Send + Sync,
|
|
||||||
B: Send + Default,
|
|
||||||
{
|
{
|
||||||
if let Some(slot_cache) = self.accounts_cache.slot_cache(slot) {
|
if let Some(slot_cache) = self.accounts_cache.slot_cache(slot) {
|
||||||
// If we see the slot in the cache, then all the account information
|
// If we see the slot in the cache, then all the account information
|
||||||
// is in this cached slot
|
// is in this cached slot
|
||||||
let mut retval = B::default();
|
if slot_cache.len() > SCAN_SLOT_PAR_ITER_THRESHOLD {
|
||||||
for cached_account in slot_cache.iter() {
|
ScanStorageResult::Cached(self.thread_pool.install(|| {
|
||||||
scan_func(
|
slot_cache
|
||||||
LoadedAccount::Cached((
|
.par_iter()
|
||||||
*cached_account.key(),
|
.filter_map(|cached_account| {
|
||||||
Cow::Borrowed(cached_account.value()),
|
cache_map_func(LoadedAccount::Cached((
|
||||||
)),
|
*cached_account.key(),
|
||||||
CACHE_VIRTUAL_STORAGE_ID,
|
Cow::Borrowed(cached_account.value()),
|
||||||
&mut retval,
|
)))
|
||||||
);
|
})
|
||||||
|
.collect()
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
ScanStorageResult::Cached(
|
||||||
|
slot_cache
|
||||||
|
.iter()
|
||||||
|
.filter_map(|cached_account| {
|
||||||
|
cache_map_func(LoadedAccount::Cached((
|
||||||
|
*cached_account.key(),
|
||||||
|
Cow::Borrowed(cached_account.value()),
|
||||||
|
)))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
vec![retval]
|
|
||||||
} else {
|
} else {
|
||||||
|
let retval = B::default();
|
||||||
// If the slot is not in the cache, then all the account information must have
|
// If the slot is not in the cache, then all the account information must have
|
||||||
// been flushed. This is guaranteed because we only remove the rooted slot from
|
// been flushed. This is guaranteed because we only remove the rooted slot from
|
||||||
// the cache *after* we've finished flushing in `flush_slot_cache`.
|
// the cache *after* we've finished flushing in `flush_slot_cache`.
|
||||||
|
@ -1933,21 +1954,12 @@ impl AccountsDB {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
self.thread_pool.install(|| {
|
self.thread_pool.install(|| {
|
||||||
storage_maps
|
storage_maps
|
||||||
.into_par_iter()
|
.par_iter()
|
||||||
.map(|storage| {
|
.flat_map(|storage| storage.accounts.accounts(0))
|
||||||
let accounts = storage.accounts.accounts(0);
|
.for_each(|account| storage_scan_func(&retval, LoadedAccount::Stored(account)));
|
||||||
let mut retval = B::default();
|
});
|
||||||
accounts.into_iter().for_each(|stored_account| {
|
|
||||||
scan_func(
|
ScanStorageResult::Stored(retval)
|
||||||
LoadedAccount::Stored(stored_account),
|
|
||||||
storage.append_vec_id(),
|
|
||||||
&mut retval,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
retval
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2518,9 +2530,10 @@ impl AccountsDB {
|
||||||
// Reads will then always read the latest version of a slot. Scans will also know
|
// Reads will then always read the latest version of a slot. Scans will also know
|
||||||
// which version their parents because banks will also be augmented with this version,
|
// which version their parents because banks will also be augmented with this version,
|
||||||
// which handles cases where a deletion of one version happens in the middle of the scan.
|
// which handles cases where a deletion of one version happens in the middle of the scan.
|
||||||
let pubkey_sets: Vec<HashSet<Pubkey>> = self.scan_account_storage(
|
let scan_result: ScanStorageResult<Pubkey, DashSet<Pubkey>> = self.scan_account_storage(
|
||||||
remove_slot,
|
remove_slot,
|
||||||
|loaded_account: LoadedAccount, _, accum: &mut HashSet<Pubkey>| {
|
|loaded_account: LoadedAccount| Some(*loaded_account.pubkey()),
|
||||||
|
|accum: &DashSet<Pubkey>, loaded_account: LoadedAccount| {
|
||||||
accum.insert(*loaded_account.pubkey());
|
accum.insert(*loaded_account.pubkey());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -2528,15 +2541,26 @@ impl AccountsDB {
|
||||||
// Purge this slot from the accounts index
|
// Purge this slot from the accounts index
|
||||||
let purge_slot: HashSet<Slot> = vec![remove_slot].into_iter().collect();
|
let purge_slot: HashSet<Slot> = vec![remove_slot].into_iter().collect();
|
||||||
let mut reclaims = vec![];
|
let mut reclaims = vec![];
|
||||||
{
|
match scan_result {
|
||||||
let pubkeys = pubkey_sets.iter().flatten();
|
ScanStorageResult::Cached(cached_keys) => {
|
||||||
for pubkey in pubkeys {
|
for pubkey in cached_keys.iter() {
|
||||||
self.accounts_index.purge_exact(
|
self.accounts_index.purge_exact(
|
||||||
pubkey,
|
pubkey,
|
||||||
&purge_slot,
|
&purge_slot,
|
||||||
&mut reclaims,
|
&mut reclaims,
|
||||||
&self.account_indexes,
|
&self.account_indexes,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScanStorageResult::Stored(stored_keys) => {
|
||||||
|
for set_ref in stored_keys.iter() {
|
||||||
|
self.accounts_index.purge_exact(
|
||||||
|
set_ref.key(),
|
||||||
|
&purge_slot,
|
||||||
|
&mut reclaims,
|
||||||
|
&self.account_indexes,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3061,13 +3085,6 @@ impl AccountsDB {
|
||||||
.swap(0, Ordering::Relaxed),
|
.swap(0, Ordering::Relaxed),
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"delta_hash_merge_us",
|
|
||||||
self.stats
|
|
||||||
.delta_hash_merge_time_total_us
|
|
||||||
.swap(0, Ordering::Relaxed),
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"delta_hash_accumulate_us",
|
"delta_hash_accumulate_us",
|
||||||
self.stats
|
self.stats
|
||||||
|
@ -3345,41 +3362,59 @@ impl AccountsDB {
|
||||||
|
|
||||||
pub fn get_accounts_delta_hash(&self, slot: Slot) -> Hash {
|
pub fn get_accounts_delta_hash(&self, slot: Slot) -> Hash {
|
||||||
let mut scan = Measure::start("scan");
|
let mut scan = Measure::start("scan");
|
||||||
let mut accumulator: Vec<HashMap<Pubkey, (u64, Hash)>> = self.scan_account_storage(
|
|
||||||
slot,
|
let scan_result: ScanStorageResult<(Pubkey, Hash, u64), DashMapVersionHash> = self
|
||||||
|loaded_account: LoadedAccount,
|
.scan_account_storage(
|
||||||
_store_id: AppendVecId,
|
slot,
|
||||||
accum: &mut HashMap<Pubkey, (u64, Hash)>| {
|
|loaded_account: LoadedAccount| {
|
||||||
accum.insert(
|
// Cache only has one version per key, don't need to worry about versioning
|
||||||
*loaded_account.pubkey(),
|
Some((
|
||||||
(
|
*loaded_account.pubkey(),
|
||||||
loaded_account.write_version(),
|
|
||||||
*loaded_account.loaded_hash(),
|
*loaded_account.loaded_hash(),
|
||||||
),
|
CACHE_VIRTUAL_WRITE_VERSION,
|
||||||
);
|
))
|
||||||
},
|
},
|
||||||
);
|
|accum: &DashMap<Pubkey, (u64, Hash)>, loaded_account: LoadedAccount| {
|
||||||
|
let loaded_write_version = loaded_account.write_version();
|
||||||
|
let loaded_hash = *loaded_account.loaded_hash();
|
||||||
|
let should_insert =
|
||||||
|
if let Some(existing_entry) = accum.get(loaded_account.pubkey()) {
|
||||||
|
loaded_write_version > existing_entry.value().version()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
if should_insert {
|
||||||
|
// Detected insertion is necessary, grabs the write lock to commit the write,
|
||||||
|
match accum.entry(*loaded_account.pubkey()) {
|
||||||
|
// Double check in case another thread interleaved a write between the read + write.
|
||||||
|
Occupied(mut occupied_entry) => {
|
||||||
|
if loaded_write_version > occupied_entry.get().version() {
|
||||||
|
occupied_entry.insert((loaded_write_version, loaded_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vacant(vacant_entry) => {
|
||||||
|
vacant_entry.insert((loaded_write_version, loaded_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
scan.stop();
|
scan.stop();
|
||||||
let mut merge = Measure::start("merge");
|
|
||||||
let mut account_maps = HashMap::new();
|
|
||||||
while let Some(maps) = accumulator.pop() {
|
|
||||||
AccountsDB::merge(&mut account_maps, &maps);
|
|
||||||
}
|
|
||||||
merge.stop();
|
|
||||||
|
|
||||||
let mut accumulate = Measure::start("accumulate");
|
let mut accumulate = Measure::start("accumulate");
|
||||||
let hashes: Vec<_> = account_maps
|
let hashes: Vec<_> = match scan_result {
|
||||||
.into_iter()
|
ScanStorageResult::Cached(cached_result) => cached_result,
|
||||||
.map(|(pubkey, (_, hash))| (pubkey, hash, 0))
|
ScanStorageResult::Stored(stored_result) => stored_result
|
||||||
.collect();
|
.into_iter()
|
||||||
|
.map(|(pubkey, (_latest_write_version, hash))| (pubkey, hash, 0))
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
let ret = Self::accumulate_account_hashes(hashes, slot, false);
|
let ret = Self::accumulate_account_hashes(hashes, slot, false);
|
||||||
accumulate.stop();
|
accumulate.stop();
|
||||||
self.stats
|
self.stats
|
||||||
.delta_hash_scan_time_total_us
|
.delta_hash_scan_time_total_us
|
||||||
.fetch_add(scan.as_us(), Ordering::Relaxed);
|
.fetch_add(scan.as_us(), Ordering::Relaxed);
|
||||||
self.stats
|
|
||||||
.delta_hash_merge_time_total_us
|
|
||||||
.fetch_add(merge.as_us(), Ordering::Relaxed);
|
|
||||||
self.stats
|
self.stats
|
||||||
.delta_hash_accumulate_time_total_us
|
.delta_hash_accumulate_time_total_us
|
||||||
.fetch_add(accumulate.as_us(), Ordering::Relaxed);
|
.fetch_add(accumulate.as_us(), Ordering::Relaxed);
|
||||||
|
@ -3880,20 +3915,6 @@ impl AccountsDB {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge<X>(dest: &mut HashMap<Pubkey, X>, source: &HashMap<Pubkey, X>)
|
|
||||||
where
|
|
||||||
X: Versioned + Clone,
|
|
||||||
{
|
|
||||||
for (key, source_item) in source.iter() {
|
|
||||||
if let Some(dest_item) = dest.get(key) {
|
|
||||||
if dest_item.version() > source_item.version() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dest.insert(*key, source_item.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_index(&self) {
|
pub fn generate_index(&self) {
|
||||||
type AccountsMap<'a> =
|
type AccountsMap<'a> =
|
||||||
DashMap<Pubkey, Mutex<BTreeMap<u64, (AppendVecId, StoredAccountMeta<'a>)>>>;
|
DashMap<Pubkey, Mutex<BTreeMap<u64, (AppendVecId, StoredAccountMeta<'a>)>>>;
|
||||||
|
|
Loading…
Reference in New Issue