accounts.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #![feature(test)]
  2. #![allow(clippy::arithmetic_side_effects)]
  3. extern crate test;
  4. use {
  5. dashmap::DashMap,
  6. rand::Rng,
  7. rayon::iter::{IntoParallelRefIterator, ParallelIterator},
  8. solana_account::{AccountSharedData, ReadableAccount},
  9. solana_accounts_db::{
  10. account_info::{AccountInfo, StorageLocation},
  11. accounts::{AccountAddressFilter, Accounts},
  12. accounts_db::{AccountFromStorage, AccountsDb, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS},
  13. accounts_index::ScanConfig,
  14. ancestors::Ancestors,
  15. },
  16. solana_hash::Hash,
  17. solana_pubkey::Pubkey,
  18. std::{
  19. collections::{HashMap, HashSet},
  20. path::PathBuf,
  21. sync::{Arc, RwLock},
  22. thread::Builder,
  23. },
  24. test::Bencher,
  25. };
  26. #[cfg(not(any(target_env = "msvc", target_os = "freebsd")))]
  27. #[global_allocator]
  28. static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
  29. fn new_accounts_db(account_paths: Vec<PathBuf>) -> AccountsDb {
  30. AccountsDb::new_with_config(
  31. account_paths,
  32. ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS,
  33. None,
  34. Arc::default(),
  35. )
  36. }
  37. #[bench]
  38. fn bench_delete_dependencies(bencher: &mut Bencher) {
  39. agave_logger::setup();
  40. let accounts_db = new_accounts_db(vec![PathBuf::from("accounts_delete_deps")]);
  41. let accounts = Accounts::new(Arc::new(accounts_db));
  42. let mut old_pubkey = Pubkey::default();
  43. let zero_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner());
  44. for i in 0..1000 {
  45. let pubkey = solana_pubkey::new_rand();
  46. let account = AccountSharedData::new(i + 1, 0, AccountSharedData::default().owner());
  47. accounts
  48. .accounts_db
  49. .store_for_tests((i, [(&pubkey, &account)].as_slice()));
  50. accounts
  51. .accounts_db
  52. .store_for_tests((i, [(&old_pubkey, &zero_account)].as_slice()));
  53. old_pubkey = pubkey;
  54. accounts.accounts_db.add_root_and_flush_write_cache(i);
  55. }
  56. bencher.iter(|| {
  57. accounts.accounts_db.clean_accounts_for_tests();
  58. });
  59. }
  60. fn store_accounts_with_possible_contention<F>(bench_name: &str, bencher: &mut Bencher, reader_f: F)
  61. where
  62. F: Fn(&Accounts, &[Pubkey]) + Send + Copy + 'static,
  63. {
  64. let num_readers = 5;
  65. let accounts_db = new_accounts_db(vec![PathBuf::from(
  66. std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string()),
  67. )
  68. .join(bench_name)]);
  69. let accounts = Arc::new(Accounts::new(Arc::new(accounts_db)));
  70. let num_keys = 1000;
  71. let slot = 0;
  72. let pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
  73. .take(num_keys)
  74. .collect();
  75. let accounts_data: Vec<_> = std::iter::repeat_n(
  76. AccountSharedData::new(1, 0, &Pubkey::new_from_array([0u8; 32])),
  77. num_keys,
  78. )
  79. .collect();
  80. let storable_accounts: Vec<_> = pubkeys.iter().zip(accounts_data.iter()).collect();
  81. accounts.store_accounts_par((slot, storable_accounts.as_slice()), None);
  82. accounts.add_root(slot);
  83. accounts
  84. .accounts_db
  85. .flush_accounts_cache_slot_for_tests(slot);
  86. let pubkeys = Arc::new(pubkeys);
  87. for i in 0..num_readers {
  88. let accounts = accounts.clone();
  89. let pubkeys = pubkeys.clone();
  90. Builder::new()
  91. .name(format!("reader{i:02}"))
  92. .spawn(move || {
  93. reader_f(&accounts, &pubkeys);
  94. })
  95. .unwrap();
  96. }
  97. let num_new_keys = 1000;
  98. bencher.iter(|| {
  99. let new_pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
  100. .take(num_new_keys)
  101. .collect();
  102. let new_storable_accounts: Vec<_> = new_pubkeys.iter().zip(accounts_data.iter()).collect();
  103. // Write to a different slot than the one being read from. Because
  104. // there's a new account pubkey being written to every time, will
  105. // compete for the accounts index lock on every store
  106. accounts.store_accounts_par((slot + 1, new_storable_accounts.as_slice()), None);
  107. });
  108. }
  109. #[bench]
  110. fn bench_concurrent_read_write(bencher: &mut Bencher) {
  111. store_accounts_with_possible_contention(
  112. "concurrent_read_write",
  113. bencher,
  114. |accounts, pubkeys| {
  115. let mut rng = rand::thread_rng();
  116. loop {
  117. let i = rng.gen_range(0..pubkeys.len());
  118. test::black_box(
  119. accounts
  120. .load_without_fixed_root(&Ancestors::default(), &pubkeys[i])
  121. .unwrap(),
  122. );
  123. }
  124. },
  125. )
  126. }
  127. #[bench]
  128. fn bench_concurrent_scan_write(bencher: &mut Bencher) {
  129. store_accounts_with_possible_contention("concurrent_scan_write", bencher, |accounts, _| loop {
  130. test::black_box(
  131. accounts
  132. .load_by_program(
  133. &Ancestors::default(),
  134. 0,
  135. AccountSharedData::default().owner(),
  136. &ScanConfig::default(),
  137. )
  138. .unwrap(),
  139. );
  140. })
  141. }
  142. #[bench]
  143. #[ignore]
  144. fn bench_dashmap_single_reader_with_n_writers(bencher: &mut Bencher) {
  145. let num_readers = 5;
  146. let num_keys = 10000;
  147. let map = Arc::new(DashMap::new());
  148. for i in 0..num_keys {
  149. map.insert(i, i);
  150. }
  151. for _ in 0..num_readers {
  152. let map = map.clone();
  153. Builder::new()
  154. .name("readers".to_string())
  155. .spawn(move || loop {
  156. test::black_box(map.entry(5).or_insert(2));
  157. })
  158. .unwrap();
  159. }
  160. bencher.iter(|| {
  161. for _ in 0..num_keys {
  162. test::black_box(map.get(&5).unwrap().value());
  163. }
  164. })
  165. }
  166. #[bench]
  167. #[ignore]
  168. fn bench_rwlock_hashmap_single_reader_with_n_writers(bencher: &mut Bencher) {
  169. let num_readers = 5;
  170. let num_keys = 10000;
  171. let map = Arc::new(RwLock::new(HashMap::new()));
  172. for i in 0..num_keys {
  173. map.write().unwrap().insert(i, i);
  174. }
  175. for _ in 0..num_readers {
  176. let map = map.clone();
  177. Builder::new()
  178. .name("readers".to_string())
  179. .spawn(move || loop {
  180. test::black_box(map.write().unwrap().get(&5));
  181. })
  182. .unwrap();
  183. }
  184. bencher.iter(|| {
  185. for _ in 0..num_keys {
  186. test::black_box(map.read().unwrap().get(&5));
  187. }
  188. })
  189. }
  190. fn setup_bench_dashmap_iter() -> (Arc<Accounts>, DashMap<Pubkey, (AccountSharedData, Hash)>) {
  191. let accounts_db = new_accounts_db(vec![PathBuf::from(
  192. std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string()),
  193. )
  194. .join("bench_dashmap_par_iter")]);
  195. let accounts = Arc::new(Accounts::new(Arc::new(accounts_db)));
  196. let dashmap = DashMap::new();
  197. let num_keys = std::env::var("NUM_BENCH_KEYS")
  198. .map(|num_keys| num_keys.parse::<usize>().unwrap())
  199. .unwrap_or_else(|_| 10000);
  200. for _ in 0..num_keys {
  201. dashmap.insert(
  202. Pubkey::new_unique(),
  203. (
  204. AccountSharedData::new(1, 0, AccountSharedData::default().owner()),
  205. Hash::new_unique(),
  206. ),
  207. );
  208. }
  209. (accounts, dashmap)
  210. }
  211. #[bench]
  212. fn bench_dashmap_par_iter(bencher: &mut Bencher) {
  213. let (accounts, dashmap) = setup_bench_dashmap_iter();
  214. bencher.iter(|| {
  215. test::black_box(accounts.accounts_db.thread_pool_foreground.install(|| {
  216. dashmap
  217. .par_iter()
  218. .map(|cached_account| (*cached_account.key(), cached_account.value().1))
  219. .collect::<Vec<(Pubkey, Hash)>>()
  220. }));
  221. });
  222. }
  223. #[bench]
  224. fn bench_dashmap_iter(bencher: &mut Bencher) {
  225. let (_accounts, dashmap) = setup_bench_dashmap_iter();
  226. bencher.iter(|| {
  227. test::black_box(
  228. dashmap
  229. .iter()
  230. .map(|cached_account| (*cached_account.key(), cached_account.value().1))
  231. .collect::<Vec<(Pubkey, Hash)>>(),
  232. );
  233. });
  234. }
  235. #[bench]
  236. fn bench_load_largest_accounts(b: &mut Bencher) {
  237. let accounts_db = new_accounts_db(Vec::new());
  238. let accounts = Accounts::new(Arc::new(accounts_db));
  239. let mut rng = rand::thread_rng();
  240. for _ in 0..10_000 {
  241. let lamports = rng.gen();
  242. let pubkey = Pubkey::new_unique();
  243. let account = AccountSharedData::new(lamports, 0, &Pubkey::default());
  244. accounts
  245. .accounts_db
  246. .store_for_tests((0, [(&pubkey, &account)].as_slice()));
  247. }
  248. accounts.accounts_db.add_root_and_flush_write_cache(0);
  249. let ancestors = Ancestors::from(vec![0]);
  250. let bank_id = 0;
  251. b.iter(|| {
  252. accounts.load_largest_accounts(
  253. &ancestors,
  254. bank_id,
  255. 20,
  256. &HashSet::new(),
  257. AccountAddressFilter::Exclude,
  258. false,
  259. )
  260. });
  261. }
  262. #[bench]
  263. fn bench_sort_and_remove_dups(b: &mut Bencher) {
  264. fn generate_sample_account_from_storage(i: u8) -> AccountFromStorage {
  265. // offset has to be 8 byte aligned
  266. let offset = (i as usize) * std::mem::size_of::<u64>();
  267. AccountFromStorage {
  268. index_info: AccountInfo::new(StorageLocation::AppendVec(i as u32, offset), i == 0),
  269. data_len: i as u64,
  270. pubkey: Pubkey::new_from_array([i; 32]),
  271. }
  272. }
  273. use rand::prelude::*;
  274. let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1234);
  275. let accounts: Vec<_> =
  276. std::iter::repeat_with(|| generate_sample_account_from_storage(rng.gen::<u8>()))
  277. .take(1000)
  278. .collect();
  279. b.iter(|| AccountsDb::sort_and_remove_dups(&mut accounts.clone()));
  280. }
  281. #[bench]
  282. fn bench_sort_and_remove_dups_no_dups(b: &mut Bencher) {
  283. fn generate_sample_account_from_storage(i: u8) -> AccountFromStorage {
  284. // offset has to be 8 byte aligned
  285. let offset = (i as usize) * std::mem::size_of::<u64>();
  286. AccountFromStorage {
  287. index_info: AccountInfo::new(StorageLocation::AppendVec(i as u32, offset), i == 0),
  288. data_len: i as u64,
  289. pubkey: Pubkey::new_unique(),
  290. }
  291. }
  292. use rand::prelude::*;
  293. let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1234);
  294. let mut accounts: Vec<_> =
  295. std::iter::repeat_with(|| generate_sample_account_from_storage(rng.gen::<u8>()))
  296. .take(1000)
  297. .collect();
  298. accounts.shuffle(&mut rng);
  299. b.iter(|| AccountsDb::sort_and_remove_dups(&mut accounts.clone()));
  300. }