Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add benchmarks for varied forms of lookups #2142

Merged
merged 15 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- [2142](https://github.com/FuelLabs/fuel-core/pull/2142): Added benchmarks for varied forms of db lookups to assist in optimizations.

## [Version 0.35.0]

### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ p256 = { version = "0.13", default-features = false, features = [
"digest",
"ecdsa",
] }
postcard = { workspace = true }
primitive-types = { workspace = true, default-features = false }
quanta = "0.12"
rand = { workspace = true }
Expand Down Expand Up @@ -66,3 +67,7 @@ name = "block_target_gas"
[[bench]]
harness = false
name = "transaction_throughput"

[[bench]]
harness = false
name = "db_lookup_times"
96 changes: 96 additions & 0 deletions benches/benches/db_lookup_times.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::db_lookup_times_utils::{
matrix::matrix,
utils::{
get_full_block,
get_random_block_height,
multi_get_block,
open_db,
open_raw_rocksdb,
},
};
use criterion::{
criterion_group,
criterion_main,
Criterion,
};
use db_lookup_times_utils::seed::{
seed_compressed_blocks_and_transactions_matrix,
seed_full_block_matrix,
};
use fuel_core_storage::transactional::AtomicView;
use rand::thread_rng;

mod db_lookup_times_utils;

pub fn header_and_tx_lookup(c: &mut Criterion) {
let method = "header_and_tx";
let mut rng = thread_rng();

seed_compressed_blocks_and_transactions_matrix(method);
let mut group = c.benchmark_group(method);

for (block_count, tx_count) in matrix() {
let database = open_db(block_count, tx_count, method);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to clean up the database after benchmarks. If you want to keep the results there, then we can add additional ENV variable that you can set to disable clean up logic

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in #2159

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let view = database.latest_view().unwrap();
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
b.iter(|| {
let height = get_random_block_height(&mut rng, block_count);
let block = view.get_full_block(&height);
assert!(block.is_ok());
assert!(block.unwrap().is_some());
});
});
}

group.finish();
}

pub fn multi_get_lookup(c: &mut Criterion) {
let method = "multi_get";
let mut rng = thread_rng();

seed_compressed_blocks_and_transactions_matrix(method);
let mut group = c.benchmark_group(method);

for (block_count, tx_count) in matrix() {
let database = open_raw_rocksdb(block_count, tx_count, method);
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
b.iter(|| {
let height = get_random_block_height(&mut rng, block_count);
assert!(multi_get_block(&database, height).is_ok());
});
});
}

group.finish();
}

pub fn full_block_lookup(c: &mut Criterion) {
let method = "full_block";
let mut rng = thread_rng();

seed_full_block_matrix();
let mut group = c.benchmark_group(method);

for (block_count, tx_count) in matrix() {
let database = open_db(block_count, tx_count, method);
let view = database.latest_view().unwrap();
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
b.iter(|| {
let height = get_random_block_height(&mut rng, block_count);
let full_block = get_full_block(&view, &height);
assert!(full_block.is_ok());
assert!(full_block.unwrap().is_some());
});
});
}

group.finish();
}

criterion_group! {
name = benches;
config = Criterion::default().sample_size(100_000).measurement_time(std::time::Duration::from_secs(100));
targets = header_and_tx_lookup, multi_get_lookup, full_block_lookup
}
criterion_main!(benches);
11 changes: 11 additions & 0 deletions benches/benches/db_lookup_times_utils/matrix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub const BLOCK_COUNT_MATRIX: [u32; 2] = [10, 100];
pub const TX_COUNT_MATRIX: [u32; 2] = [100, 1000];

// todo: we can make this lazy loaded
netrome marked this conversation as resolved.
Show resolved Hide resolved
pub fn matrix() -> impl Iterator<Item = (u32, u32)> {
BLOCK_COUNT_MATRIX.iter().flat_map(|&block_count| {
TX_COUNT_MATRIX
.iter()
.map(move |&tx_count| (block_count, tx_count))
})
}
3 changes: 3 additions & 0 deletions benches/benches/db_lookup_times_utils/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod matrix;
pub mod seed;
pub mod utils;
153 changes: 153 additions & 0 deletions benches/benches/db_lookup_times_utils/seed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use crate::db_lookup_times_utils::{
matrix::matrix,
utils::{
chain_id,
open_raw_rocksdb,
},
};
use fuel_core::{
database::database_description::on_chain::OnChain,
state::rocks_db::RocksDb,
};
use fuel_core_storage::{
column::Column,
kv_store::{
KeyValueMutate,
Value,
},
};
use fuel_core_types::{
blockchain::{
block::{
Block,
PartialFuelBlock,
},
header::{
ConsensusHeader,
PartialBlockHeader,
},
primitives::Empty,
},
fuel_tx::{
Transaction,
UniqueIdentifier,
},
fuel_types::BlockHeight,
};

pub fn seed_compressed_blocks_and_transactions_matrix(method: &str) {
for (block_count, tx_count) in matrix() {
let mut database = open_raw_rocksdb(block_count, tx_count, method);
let _ =
seed_compressed_blocks_and_transactions(&mut database, block_count, tx_count);
}
}

pub fn seed_full_block_matrix() {
for (block_count, tx_count) in matrix() {
let mut database = open_raw_rocksdb(block_count, tx_count, "full_block");
seed_full_blocks(&mut database, block_count, tx_count);
}
}

fn generate_bench_block(height: u32, tx_count: u32) -> Block {
let header = PartialBlockHeader {
application: Default::default(),
consensus: ConsensusHeader::<Empty> {
height: BlockHeight::from(height),
..Default::default()
},
};
let txes = generate_bench_transactions(tx_count);
let block = PartialFuelBlock::new(header, txes);
block.generate(&[], Default::default()).unwrap()
}

fn generate_bench_transactions(tx_count: u32) -> Vec<Transaction> {
let mut txes = vec![];
for _ in 0..tx_count {
txes.push(Transaction::default_test_tx());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] It would be nice to make transactions random to be sure that compression will not affect it too much

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under the hood the transactions are randomized ~ not sure if I understand correctly!
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_random_fee_input always return the same result=D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, okay ~ will alter

}
txes
}

fn height_key(block_height: u32) -> Vec<u8> {
BlockHeight::from(block_height).to_bytes().to_vec()
}

fn insert_compressed_block(
database: &mut RocksDb<OnChain>,
height: u32,
tx_count: u32,
) -> Block {
let block = generate_bench_block(height, tx_count);

let compressed_block = block.compress(&chain_id());
let height_key = height_key(height);

let raw_compressed_block = postcard::to_allocvec(&compressed_block).unwrap().to_vec();
let raw_transactions = block
.transactions()
.iter()
.map(|tx| {
(
tx.id(&chain_id()),
postcard::to_allocvec(tx).unwrap().to_vec(),
)
})
.collect::<Vec<_>>();

// 1. insert into CompressedBlocks table
database
.put(
height_key.as_slice(),
Column::FuelBlocks,
Value::new(raw_compressed_block),
)
.unwrap();
// 2. insert into Transactions table
for (tx_id, tx) in raw_transactions {
database
.put(tx_id.as_slice(), Column::Transactions, Value::new(tx))
.unwrap();
}

block
}

fn insert_full_block(database: &mut RocksDb<OnChain>, height: u32, tx_count: u32) {
let block = insert_compressed_block(database, height, tx_count);

let height_key = height_key(height);
let raw_full_block = postcard::to_allocvec(&block).unwrap().to_vec();

// 3. insert into FullFuelBlocks table
rymnc marked this conversation as resolved.
Show resolved Hide resolved
database
.put(
height_key.as_slice(),
Column::FullFuelBlocks,
Value::new(raw_full_block),
)
.unwrap();
}

fn seed_compressed_blocks_and_transactions(
database: &mut RocksDb<OnChain>,
block_count: u32,
tx_count: u32,
) -> Vec<Block> {
let mut blocks = vec![];
for block_number in 0..block_count {
blocks.push(insert_compressed_block(database, block_number, tx_count));
}
blocks
}

fn seed_full_blocks(database: &mut RocksDb<OnChain>, block_count: u32, tx_count: u32) {
// we seed compressed blocks and transactions to not affect individual
// lookup times
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't make sense to me inside the context of seed_full_blocks. I'm also not sure what it means to "affect individual lookup times".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the context is that if we do include FullFuelBlocks in the codebase at some point and use it, we want to use it in tandem with the FuelBlocks and Transactions tables so as to not affect their individual lookup times, i.e cases where we make individual block header calls (to get the block height, for example), and where we make individual tx lookups.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand now. I think this comment should be above the insert_full_block in that case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in 7cca278


for block_number in 0..block_count {
insert_full_block(database, block_number, tx_count);
}
}
Loading
Loading