From 5c261ee9897282ab4e5bbee6f79a030bac5d217d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Sat, 9 Nov 2024 15:19:29 +0700 Subject: [PATCH 1/4] feat: simple bench target for measuring coin batch lookup times --- Cargo.lock | 1 + benches/Cargo.toml | 5 + benches/benches/read_view_get_coins.rs | 156 +++++++++++++++++++++++++ crates/fuel-core/src/state/rocks_db.rs | 4 +- 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 benches/benches/read_view_get_coins.rs diff --git a/Cargo.lock b/Cargo.lock index 67a97f72c3e..33c27003ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3263,6 +3263,7 @@ dependencies = [ "ethnum", "fuel-core", "fuel-core-chain-config", + "fuel-core-client", "fuel-core-database", "fuel-core-services", "fuel-core-storage", diff --git a/benches/Cargo.toml b/benches/Cargo.toml index e4ee7db927b..a44309a48d0 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -23,6 +23,7 @@ fuel-core = { path = "../crates/fuel-core", default-features = false, features = "rocksdb-production", ] } fuel-core-chain-config = { workspace = true } +fuel-core-client = { path = "./../crates/client" } fuel-core-database = { path = "./../crates/database" } fuel-core-services = { path = "./../crates/services" } fuel-core-storage = { path = "./../crates/storage", features = ["smt"] } @@ -76,3 +77,7 @@ name = "transaction_throughput" [[bench]] harness = false name = "db_lookup_times" + +[[bench]] +harness = false +name = "read_view_get_coins" diff --git a/benches/benches/read_view_get_coins.rs b/benches/benches/read_view_get_coins.rs new file mode 100644 index 00000000000..0327b889371 --- /dev/null +++ b/benches/benches/read_view_get_coins.rs @@ -0,0 +1,156 @@ +use fuel_core::{ + combined_database::CombinedDatabase, + fuel_core_graphql_api::database::ReadDatabase, +}; + +use fuel_core_chain_config::Randomize; +use fuel_core_storage::{ + tables::{ + Coins, + FuelBlocks, + }, + transactional::WriteTransaction, + StorageAsMut, +}; +use fuel_core_types::{ + blockchain::block::CompressedBlock, + entities::coins::coin::CompressedCoin, + fuel_tx::{ + Address, + AssetId, + UtxoId, + }, + fuel_types::BlockHeight, +}; +use rand::{ + rngs::StdRng, + SeedableRng, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + println!("Setting up bench harness."); + let mut harness = Harness::new(StdRng::seed_from_u64(2322)).await?; + + println!("Populating storage with transactions."); + let utxo_ids = harness.populate_database().await?; + + println!("Querying transactions from storage."); + let total = harness + .query_database_many_times_concurrent(&utxo_ids) + .await?; + + println!("Queried a total of {total} coins"); + + Ok(()) +} + +struct Harness { + rng: Rng, + params: Parameters, + db: CombinedDatabase, +} + +impl Harness { + async fn new(rng: Rng) -> anyhow::Result { + let params = Parameters::hard_coded(); + let db = CombinedDatabase::default(); + + Ok(Self { rng, params, db }) + } + + async fn populate_database(&mut self) -> anyhow::Result> { + let mut utxo_ids = Vec::new(); + + let mut coins: Vec<_> = (0..(self.params.utxo_count_per_block + * self.params.num_blocks)) + .map(|_| self.generate_random_coin()) + .collect(); + + for block_height in 0..self.params.num_blocks { + let block_height = BlockHeight::from(block_height as u32); + let mut compressed_block = CompressedBlock::default(); + compressed_block.header_mut().set_block_height(block_height); + + let mut transaction = self.db.on_chain_mut().write_transaction(); + + transaction + .storage::() + .insert(&block_height, &compressed_block) + .unwrap(); + + for _ in 0..self.params.utxo_count_per_block { + let (utxo_id, coin) = coins.pop().unwrap(); // TODO: Cleanup + + transaction + .storage::() + .insert(&utxo_id, &coin) + .unwrap(); + + utxo_ids.push(utxo_id); + } + + transaction.commit().unwrap(); + } + + Ok(utxo_ids) + } + + async fn query_database_many_times_concurrent( + &mut self, + utxo_ids: &[UtxoId], + ) -> anyhow::Result { + let mut total = 0; + let mut handles = Vec::new(); + for _ in 0..self.params.num_queries { + let on_chain_db = self.db.on_chain().clone(); + let off_chain_db = self.db.off_chain().clone(); + let utxo_ids = utxo_ids.to_vec(); + + let handle = tokio::spawn(async move { + let read_database = + ReadDatabase::new(0, BlockHeight::new(0), on_chain_db, off_chain_db); + + let read_view = read_database.view().unwrap(); + let res: Vec<_> = read_view.coins(utxo_ids).await.collect(); + res.len() + }); + + handles.push(handle); + } + + for handle in handles { + let res = handle.await.unwrap(); + total += res; + } + + Ok(total) + } + + fn generate_random_coin(&mut self) -> (UtxoId, CompressedCoin) { + let utxo_id = UtxoId::randomize(&mut self.rng); + + let mut coin = CompressedCoin::default(); + coin.set_amount(self.rng.next_u64()); + coin.set_asset_id(AssetId::randomize(&mut self.rng)); + coin.set_owner(Address::randomize(&mut self.rng)); + + (utxo_id, coin) + } +} + +struct Parameters { + num_queries: usize, + num_blocks: usize, + utxo_count_per_block: usize, +} + +impl Parameters { + fn hard_coded() -> Self { + Self { + num_queries: 100, + num_blocks: 100, + utxo_count_per_block: 1000, + } + } +} diff --git a/crates/fuel-core/src/state/rocks_db.rs b/crates/fuel-core/src/state/rocks_db.rs index c751ebe7c55..8bde0eb979d 100644 --- a/crates/fuel-core/src/state/rocks_db.rs +++ b/crates/fuel-core/src/state/rocks_db.rs @@ -227,7 +227,7 @@ where opener: F, path: P, columns: Vec, - capacity: Option, + _capacity: Option, max_fds: i32, ) -> DatabaseResult where @@ -252,6 +252,8 @@ where // See https://github.com/facebook/rocksdb/blob/a1523efcdf2f0e8133b9a9f6e170a0dad49f928f/include/rocksdb/table.h#L246-L271 for details on what the format versions are/do. block_opts.set_format_version(5); + let capacity = Some(100 * 1024 * 1024); + if let Some(capacity) = capacity { // Set cache size 1/3 of the capacity as recommended by // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning#block-cache-size From 5717e59a0061bd7e0d6276513c2ebccc72a2f17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 18 Nov 2024 09:24:22 +0100 Subject: [PATCH 2/4] feat: Shuffle utxo IDs --- benches/benches/read_view_get_coins.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/benches/benches/read_view_get_coins.rs b/benches/benches/read_view_get_coins.rs index 0327b889371..75862a02103 100644 --- a/benches/benches/read_view_get_coins.rs +++ b/benches/benches/read_view_get_coins.rs @@ -24,6 +24,7 @@ use fuel_core_types::{ }; use rand::{ rngs::StdRng, + seq::SliceRandom, SeedableRng, }; @@ -93,6 +94,9 @@ impl Harness { transaction.commit().unwrap(); } + // Shuffle so random reads aren't sequential + utxo_ids.shuffle(&mut self.rng); + Ok(utxo_ids) } @@ -105,7 +109,8 @@ impl Harness { for _ in 0..self.params.num_queries { let on_chain_db = self.db.on_chain().clone(); let off_chain_db = self.db.off_chain().clone(); - let utxo_ids = utxo_ids.to_vec(); + let mut utxo_ids = utxo_ids.to_vec(); + utxo_ids.shuffle(&mut self.rng); let handle = tokio::spawn(async move { let read_database = From 87c4b01d1a5460dcdbc8002ad73ed8558e290c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 18 Nov 2024 21:29:51 +0100 Subject: [PATCH 3/4] feat: Only query a subset of all utxos each time --- benches/benches/read_view_get_coins.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/benches/benches/read_view_get_coins.rs b/benches/benches/read_view_get_coins.rs index 75862a02103..c622686141e 100644 --- a/benches/benches/read_view_get_coins.rs +++ b/benches/benches/read_view_get_coins.rs @@ -111,6 +111,7 @@ impl Harness { let off_chain_db = self.db.off_chain().clone(); let mut utxo_ids = utxo_ids.to_vec(); utxo_ids.shuffle(&mut self.rng); + utxo_ids = utxo_ids[0..self.params.num_utxos_to_query].to_vec(); let handle = tokio::spawn(async move { let read_database = @@ -146,6 +147,7 @@ impl Harness { struct Parameters { num_queries: usize, + num_utxos_to_query: usize, num_blocks: usize, utxo_count_per_block: usize, } @@ -153,8 +155,9 @@ struct Parameters { impl Parameters { fn hard_coded() -> Self { Self { - num_queries: 100, - num_blocks: 100, + num_queries: 1000, + num_utxos_to_query: 10000, + num_blocks: 1000, utxo_count_per_block: 1000, } } From c1f260569100e161fc09dc076d267b678a278d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 18 Nov 2024 22:37:24 +0100 Subject: [PATCH 4/4] feat: Use `.choose()` to select random utxos + parameter tuning --- benches/benches/read_view_get_coins.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/benches/benches/read_view_get_coins.rs b/benches/benches/read_view_get_coins.rs index c622686141e..3d1708a50dc 100644 --- a/benches/benches/read_view_get_coins.rs +++ b/benches/benches/read_view_get_coins.rs @@ -94,9 +94,6 @@ impl Harness { transaction.commit().unwrap(); } - // Shuffle so random reads aren't sequential - utxo_ids.shuffle(&mut self.rng); - Ok(utxo_ids) } @@ -109,9 +106,11 @@ impl Harness { for _ in 0..self.params.num_queries { let on_chain_db = self.db.on_chain().clone(); let off_chain_db = self.db.off_chain().clone(); - let mut utxo_ids = utxo_ids.to_vec(); - utxo_ids.shuffle(&mut self.rng); - utxo_ids = utxo_ids[0..self.params.num_utxos_to_query].to_vec(); + + let utxo_ids = utxo_ids + .choose_multiple(&mut self.rng, self.params.num_utxos_to_query) + .cloned() + .collect(); let handle = tokio::spawn(async move { let read_database = @@ -156,9 +155,9 @@ impl Parameters { fn hard_coded() -> Self { Self { num_queries: 1000, - num_utxos_to_query: 10000, + num_utxos_to_query: 10_000, num_blocks: 1000, - utxo_count_per_block: 1000, + utxo_count_per_block: 10_000, } } }