accounts_clean: Convert stack dependency calculation with iterative (#11067) (#11076)

* accounts_clean: Convert stack dependency calculation with iterative

* optimize clean with by creating a reverse-lookup hashset of the affected
keys

* Add dependency bench

reduce bench

* Huge clean

(cherry picked from commit 8bf3a0aa05)

Co-authored-by: sakridge <sakridge@gmail.com>
This commit is contained in:
mergify[bot]
2020-07-15 15:13:33 +00:00
committed by GitHub
parent 1860aacd1f
commit d718ab2491
3 changed files with 83 additions and 51 deletions

View File

@ -95,3 +95,22 @@ fn test_accounts_delta_hash(bencher: &mut Bencher) {
accounts.accounts_db.get_accounts_delta_hash(0); accounts.accounts_db.get_accounts_delta_hash(0);
}); });
} }
#[bench]
fn bench_delete_dependencies(bencher: &mut Bencher) {
solana_logger::setup();
let accounts = Accounts::new(vec![PathBuf::from("accounts_delete_deps")]);
let mut old_pubkey = Pubkey::default();
let zero_account = Account::new(0, 0, &Account::default().owner);
for i in 0..1000 {
let pubkey = Pubkey::new_rand();
let account = Account::new((i + 1) as u64, 0, &Account::default().owner);
accounts.store_slow(i, &pubkey, &account);
accounts.store_slow(i, &old_pubkey, &zero_account);
old_pubkey = pubkey;
accounts.add_root(i);
}
bencher.iter(|| {
accounts.accounts_db.clean_accounts();
});
}

View File

@ -1719,4 +1719,26 @@ mod tests {
assert!(!Accounts::has_duplicates(&[1, 2])); assert!(!Accounts::has_duplicates(&[1, 2]));
assert!(Accounts::has_duplicates(&[1, 2, 1])); assert!(Accounts::has_duplicates(&[1, 2, 1]));
} }
#[test]
fn huge_clean() {
solana_logger::setup();
let accounts = Accounts::new(Vec::new());
let mut old_pubkey = Pubkey::default();
let zero_account = Account::new(0, 0, &Account::default().owner);
info!("storing..");
for i in 0..2_000 {
let pubkey = Pubkey::new_rand();
let account = Account::new((i + 1) as u64, 0, &Account::default().owner);
accounts.store_slow(i, &pubkey, &account);
accounts.store_slow(i, &old_pubkey, &zero_account);
old_pubkey = pubkey;
accounts.add_root(i);
if i % 1_000 == 0 {
info!(" store {}", i);
}
}
info!("done..cleaning..");
accounts.accounts_db.clean_accounts();
}
} }

View File

@ -550,41 +550,9 @@ impl AccountsDB {
.extend(previous_roots); .extend(previous_roots);
} }
fn inc_store_counts(
no_delete_id: AppendVecId,
purges: &HashMap<Pubkey, (SlotList<AccountInfo>, u64)>,
store_counts: &mut HashMap<AppendVecId, usize>,
already_counted: &mut HashSet<AppendVecId>,
) {
if already_counted.contains(&no_delete_id) {
return;
}
*store_counts.get_mut(&no_delete_id).unwrap() += 1;
already_counted.insert(no_delete_id);
let mut affected_pubkeys = HashSet::new();
for (key, (account_infos, _ref_count)) in purges {
for (_slot, account_info) in account_infos {
if account_info.store_id == no_delete_id {
affected_pubkeys.insert(key);
break;
}
}
}
for key in affected_pubkeys {
for (_slot, account_info) in &purges.get(&key).unwrap().0 {
Self::inc_store_counts(
account_info.store_id,
purges,
store_counts,
already_counted,
);
}
}
}
fn calc_delete_dependencies( fn calc_delete_dependencies(
purges: &HashMap<Pubkey, (SlotList<AccountInfo>, u64)>, purges: &HashMap<Pubkey, (SlotList<AccountInfo>, u64)>,
store_counts: &mut HashMap<AppendVecId, usize>, store_counts: &mut HashMap<AppendVecId, (usize, HashSet<Pubkey>)>,
) { ) {
// Another pass to check if there are some filtered accounts which // Another pass to check if there are some filtered accounts which
// do not match the criteria of deleting all appendvecs which contain them // do not match the criteria of deleting all appendvecs which contain them
@ -596,7 +564,7 @@ impl AccountsDB {
} else { } else {
let mut no_delete = false; let mut no_delete = false;
for (_slot, account_info) in account_infos { for (_slot, account_info) in account_infos {
if *store_counts.get(&account_info.store_id).unwrap() != 0 { if store_counts.get(&account_info.store_id).unwrap().0 != 0 {
no_delete = true; no_delete = true;
break; break;
} }
@ -604,13 +572,29 @@ impl AccountsDB {
no_delete no_delete
}; };
if no_delete { if no_delete {
let mut pending_store_ids: HashSet<usize> = HashSet::new();
for (_slot_id, account_info) in account_infos { for (_slot_id, account_info) in account_infos {
Self::inc_store_counts( if !already_counted.contains(&account_info.store_id) {
account_info.store_id, pending_store_ids.insert(account_info.store_id);
&purges, }
store_counts, }
&mut already_counted, while !pending_store_ids.is_empty() {
); let id = pending_store_ids.iter().next().cloned().unwrap();
pending_store_ids.remove(&id);
if already_counted.contains(&id) {
continue;
}
store_counts.get_mut(&id).unwrap().0 += 1;
already_counted.insert(id);
let affected_pubkeys = &store_counts.get(&id).unwrap().1;
for key in affected_pubkeys {
for (_slot, account_info) in &purges.get(&key).unwrap().0 {
if !already_counted.contains(&account_info.store_id) {
pending_store_ids.insert(account_info.store_id);
}
}
}
} }
} }
} }
@ -693,18 +677,21 @@ impl AccountsDB {
// Calculate store counts as if everything was purged // Calculate store counts as if everything was purged
// Then purge if we can // Then purge if we can
let mut store_counts: HashMap<AppendVecId, usize> = HashMap::new(); let mut store_counts: HashMap<AppendVecId, (usize, HashSet<Pubkey>)> = HashMap::new();
let storage = self.storage.read().unwrap(); let storage = self.storage.read().unwrap();
for (account_infos, _ref_count) in purges.values() { for (key, (account_infos, _ref_count)) in &purges {
for (slot, account_info) in account_infos { for (slot, account_info) in account_infos {
let slot_storage = storage.0.get(&slot).unwrap(); let slot_storage = storage.0.get(&slot).unwrap();
let store = slot_storage.get(&account_info.store_id).unwrap(); let store = slot_storage.get(&account_info.store_id).unwrap();
if let Some(store_count) = store_counts.get_mut(&account_info.store_id) { if let Some(store_count) = store_counts.get_mut(&account_info.store_id) {
*store_count -= 1; store_count.0 -= 1;
store_count.1.insert(*key);
} else { } else {
let mut key_set = HashSet::new();
key_set.insert(*key);
store_counts.insert( store_counts.insert(
account_info.store_id, account_info.store_id,
store.count_and_status.read().unwrap().0 - 1, (store.count_and_status.read().unwrap().0 - 1, key_set),
); );
} }
} }
@ -721,7 +708,7 @@ impl AccountsDB {
let mut purge_filter = Measure::start("purge_filter"); let mut purge_filter = Measure::start("purge_filter");
purges.retain(|_pubkey, (account_infos, _ref_count)| { purges.retain(|_pubkey, (account_infos, _ref_count)| {
for (_slot, account_info) in account_infos.iter() { for (_slot, account_info) in account_infos.iter() {
if *store_counts.get(&account_info.store_id).unwrap() != 0 { if store_counts.get(&account_info.store_id).unwrap().0 != 0 {
return false; return false;
} }
} }
@ -4161,18 +4148,22 @@ pub mod tests {
} }
let mut store_counts = HashMap::new(); let mut store_counts = HashMap::new();
store_counts.insert(0, 0); store_counts.insert(0, (0, HashSet::from_iter(vec![key0])));
store_counts.insert(1, 0); store_counts.insert(1, (0, HashSet::from_iter(vec![key0, key1])));
store_counts.insert(2, 0); store_counts.insert(2, (0, HashSet::from_iter(vec![key1, key2])));
store_counts.insert(3, 1); store_counts.insert(3, (1, HashSet::from_iter(vec![key2])));
AccountsDB::calc_delete_dependencies(&purges, &mut store_counts); AccountsDB::calc_delete_dependencies(&purges, &mut store_counts);
let mut stores: Vec<_> = store_counts.keys().cloned().collect(); let mut stores: Vec<_> = store_counts.keys().cloned().collect();
stores.sort(); stores.sort();
for store in &stores { for store in &stores {
info!("store: {:?} : {}", store, store_counts.get(&store).unwrap()); info!(
"store: {:?} : {:?}",
store,
store_counts.get(&store).unwrap()
);
} }
for x in 0..3 { for x in 0..3 {
assert!(store_counts[&x] >= 1); assert!(store_counts[&x].0 >= 1);
} }
} }
} }