Skip to content

Commit 42fea47

Browse files
committed
feat(benches): add benchmarks for varied forms of db lookups
1 parent 9f96573 commit 42fea47

File tree

13 files changed

+453
-36
lines changed

13 files changed

+453
-36
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased]
88

9+
### Added
10+
- [2142](https://github.com/FuelLabs/fuel-core/pull/2142): Added benchmarks for varied forms of db lookups to assist in optimizations.
11+
912
## [Version 0.35.0]
1013

1114
### Added

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benches/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ p256 = { version = "0.13", default-features = false, features = [
3434
"digest",
3535
"ecdsa",
3636
] }
37+
postcard = { workspace = true }
3738
primitive-types = { workspace = true, default-features = false }
3839
quanta = "0.12"
3940
rand = { workspace = true }
@@ -66,3 +67,7 @@ name = "block_target_gas"
6667
[[bench]]
6768
harness = false
6869
name = "transaction_throughput"
70+
71+
[[bench]]
72+
harness = false
73+
name = "db_lookup_times"

benches/benches/db_lookup_times.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::db_lookup_times_utils::{
2+
matrix::matrix,
3+
utils::{
4+
get_full_block,
5+
get_random_block_height,
6+
multi_get_block,
7+
open_db,
8+
open_raw_rocksdb,
9+
},
10+
};
11+
use criterion::{
12+
criterion_group,
13+
criterion_main,
14+
Criterion,
15+
};
16+
use db_lookup_times_utils::seed::{
17+
seed_compressed_blocks_and_transactions_matrix,
18+
seed_full_block_matrix,
19+
};
20+
use fuel_core_storage::transactional::AtomicView;
21+
22+
mod db_lookup_times_utils;
23+
24+
pub fn header_and_tx_lookup(c: &mut Criterion) {
25+
let method = "header_and_tx";
26+
27+
seed_compressed_blocks_and_transactions_matrix(method);
28+
let mut group = c.benchmark_group(method);
29+
30+
for (block_count, tx_count) in matrix() {
31+
let database = open_db(block_count, tx_count, method);
32+
let height = get_random_block_height(block_count);
33+
let view = database.latest_view().unwrap();
34+
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
35+
b.iter(|| {
36+
let block = (&view).get_full_block(&height);
37+
assert!(block.is_ok());
38+
assert!(block.unwrap().is_some());
39+
});
40+
});
41+
}
42+
43+
group.finish();
44+
}
45+
46+
pub fn multi_get_lookup(c: &mut Criterion) {
47+
let method = "multi_get";
48+
49+
seed_compressed_blocks_and_transactions_matrix(method);
50+
let mut group = c.benchmark_group(method);
51+
52+
for (block_count, tx_count) in matrix() {
53+
let database = open_raw_rocksdb(block_count, tx_count, method);
54+
let height = get_random_block_height(block_count);
55+
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
56+
b.iter(|| {
57+
// todo: clean up
58+
multi_get_block(&database, height);
59+
});
60+
});
61+
}
62+
63+
group.finish();
64+
}
65+
66+
pub fn full_block_lookup(c: &mut Criterion) {
67+
let method = "full_block";
68+
69+
seed_full_block_matrix();
70+
let mut group = c.benchmark_group(method);
71+
72+
for (block_count, tx_count) in matrix() {
73+
let database = open_db(block_count, tx_count, method);
74+
let height = get_random_block_height(block_count);
75+
let view = database.latest_view().unwrap();
76+
group.bench_function(format!("{block_count}/{tx_count}"), |b| {
77+
b.iter(|| {
78+
let full_block = get_full_block(&view, &height);
79+
assert!(full_block.is_ok());
80+
assert!(full_block.unwrap().is_some());
81+
});
82+
});
83+
}
84+
85+
group.finish();
86+
}
87+
88+
criterion_group! {
89+
name = benches;
90+
config = Criterion::default().sample_size(10).measurement_time(std::time::Duration::from_secs(10));
91+
targets = header_and_tx_lookup, multi_get_lookup, full_block_lookup
92+
}
93+
criterion_main!(benches);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pub const BLOCK_COUNT_MATRIX: [u32; 1] = [1];
2+
pub const TX_COUNT_MATRIX: [u32; 1] = [1];
3+
4+
// todo: we can make this lazy loaded
5+
pub fn matrix() -> Box<dyn Iterator<Item = (u32, u32)>> {
6+
let iter = BLOCK_COUNT_MATRIX.iter().flat_map(|&block_count| {
7+
TX_COUNT_MATRIX
8+
.iter()
9+
.map(move |&tx_count| (block_count, tx_count))
10+
});
11+
12+
Box::new(iter)
13+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod matrix;
2+
pub mod seed;
3+
pub mod utils;
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use crate::db_lookup_times_utils::{
2+
matrix::matrix,
3+
utils::open_raw_rocksdb,
4+
};
5+
use fuel_core::{
6+
database::database_description::on_chain::OnChain,
7+
state::rocks_db::RocksDb,
8+
};
9+
use fuel_core_storage::{
10+
column::Column,
11+
kv_store::{
12+
KeyValueMutate,
13+
Value,
14+
},
15+
};
16+
use fuel_core_types::{
17+
blockchain::{
18+
block::{
19+
Block,
20+
PartialFuelBlock,
21+
},
22+
header::{
23+
ConsensusHeader,
24+
PartialBlockHeader,
25+
},
26+
primitives::Empty,
27+
},
28+
fuel_tx::{
29+
Transaction,
30+
UniqueIdentifier,
31+
},
32+
fuel_types::{
33+
BlockHeight,
34+
ChainId,
35+
},
36+
};
37+
38+
fn generate_bench_block(height: u32, tx_count: u32) -> Block {
39+
let header = PartialBlockHeader {
40+
application: Default::default(),
41+
consensus: ConsensusHeader::<Empty> {
42+
height: BlockHeight::from(height),
43+
..Default::default()
44+
},
45+
};
46+
let txes = generate_bench_transactions(tx_count);
47+
let block = PartialFuelBlock::new(header, txes);
48+
block.generate(&[], Default::default()).unwrap()
49+
}
50+
51+
fn generate_bench_transactions(tx_count: u32) -> Vec<Transaction> {
52+
let mut txes = vec![];
53+
for _ in 0..tx_count {
54+
txes.push(Transaction::default_test_tx());
55+
}
56+
txes
57+
}
58+
59+
fn height_key(block_height: u32) -> Vec<u8> {
60+
BlockHeight::from(block_height).to_bytes().to_vec()
61+
}
62+
63+
fn insert_compressed_block(
64+
database: &mut RocksDb<OnChain>,
65+
height: u32,
66+
tx_count: u32,
67+
) -> Block {
68+
let block = generate_bench_block(height, tx_count);
69+
70+
let compressed_block = block.compress(&ChainId::default());
71+
let height_key = height_key(height);
72+
73+
let raw_compressed_block = postcard::to_allocvec(&compressed_block).unwrap().to_vec();
74+
let raw_transactions = block
75+
.transactions()
76+
.into_iter()
77+
.map(|tx| {
78+
(
79+
tx.id(&ChainId::default()),
80+
postcard::to_allocvec(tx).unwrap().to_vec(),
81+
)
82+
})
83+
.collect::<Vec<_>>();
84+
85+
// 1. insert into CompressedBlocks table
86+
database
87+
.put(
88+
height_key.as_slice(),
89+
Column::FuelBlocks,
90+
Value::new(raw_compressed_block),
91+
)
92+
.unwrap();
93+
// 2. insert into Transactions table
94+
for (tx_id, tx) in raw_transactions {
95+
database
96+
.put(tx_id.as_slice(), Column::Transactions, Value::new(tx))
97+
.unwrap();
98+
}
99+
100+
block
101+
}
102+
103+
fn insert_full_block(database: &mut RocksDb<OnChain>, height: u32, tx_count: u32) {
104+
let block = match insert_compressed_block(database, height, tx_count) {
105+
Block::V1(b) => b,
106+
};
107+
108+
let height_key = height_key(height);
109+
let raw_full_block = postcard::to_allocvec(&block).unwrap().to_vec();
110+
111+
// 3. insert into FullFuelBlocks table
112+
database
113+
.put(
114+
height_key.as_slice(),
115+
Column::FullFuelBlocks,
116+
Value::new(raw_full_block),
117+
)
118+
.unwrap();
119+
}
120+
121+
fn seed_compressed_blocks_and_transactions(
122+
database: &mut RocksDb<OnChain>,
123+
block_count: u32,
124+
tx_count: u32,
125+
) -> Vec<Block> {
126+
let mut blocks = vec![];
127+
for block_number in 0..block_count {
128+
blocks.push(insert_compressed_block(database, block_number, tx_count));
129+
}
130+
blocks
131+
}
132+
133+
pub fn seed_compressed_blocks_and_transactions_matrix(method: &str) {
134+
for (block_count, tx_count) in matrix() {
135+
let mut database = open_raw_rocksdb(block_count, tx_count, method);
136+
let _ =
137+
seed_compressed_blocks_and_transactions(&mut database, block_count, tx_count);
138+
}
139+
}
140+
141+
fn seed_full_blocks(database: &mut RocksDb<OnChain>, block_count: u32, tx_count: u32) {
142+
// we seed compressed blocks and transactions to not affect individual
143+
// lookup times
144+
145+
for block_number in 0..block_count {
146+
insert_full_block(database, block_number, tx_count);
147+
}
148+
}
149+
150+
pub fn seed_full_block_matrix() {
151+
for (block_count, tx_count) in matrix() {
152+
let mut database = open_raw_rocksdb(block_count, tx_count, "full_block");
153+
seed_full_blocks(&mut database, block_count, tx_count);
154+
}
155+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use fuel_core::{
2+
database::{
3+
database_description::on_chain::OnChain,
4+
Database,
5+
},
6+
state::{
7+
historical_rocksdb::StateRewindPolicy,
8+
rocks_db::RocksDb,
9+
IterableKeyValueView,
10+
},
11+
};
12+
use fuel_core_storage::{
13+
column::Column,
14+
kv_store::{
15+
KeyValueInspect,
16+
StorageColumn,
17+
},
18+
tables::FullFuelBlocks,
19+
StorageAsRef,
20+
};
21+
use fuel_core_types::{
22+
blockchain::block::{
23+
Block,
24+
CompressedBlock,
25+
},
26+
fuel_tx::Transaction,
27+
fuel_types::BlockHeight,
28+
};
29+
use rand::Rng;
30+
use std::{
31+
borrow::Cow,
32+
path::Path,
33+
};
34+
35+
pub fn get_random_block_height(block_count: u32) -> BlockHeight {
36+
BlockHeight::from(rand::thread_rng().gen_range(0..block_count))
37+
}
38+
39+
pub fn open_db(block_count: u32, tx_count: u32, method: &str) -> Database {
40+
Database::open_rocksdb(
41+
Path::new(format!("./{block_count}/{method}/{tx_count}").as_str()),
42+
None, // no caching
43+
StateRewindPolicy::NoRewind,
44+
)
45+
.unwrap()
46+
}
47+
48+
pub fn open_raw_rocksdb(
49+
block_count: u32,
50+
tx_count: u32,
51+
method: &str,
52+
) -> RocksDb<OnChain> {
53+
RocksDb::default_open(
54+
Path::new(format!("./{block_count}/{method}/{tx_count}").as_str()),
55+
None,
56+
)
57+
.unwrap()
58+
}
59+
60+
pub fn get_full_block(
61+
view: &IterableKeyValueView<Column>,
62+
height: &BlockHeight,
63+
) -> Result<Option<Block>, anyhow::Error> {
64+
let db_block = view.storage::<FullFuelBlocks>().get(height)?;
65+
if let Some(block) = db_block {
66+
Ok(Some(Block::V1(Cow::into_owned(block))))
67+
} else {
68+
Ok(None)
69+
}
70+
}
71+
72+
pub fn multi_get_block(database: &RocksDb<OnChain>, height: BlockHeight) {
73+
let height_key = height.to_bytes();
74+
75+
let raw_block = database
76+
.get(&height_key, Column::FuelBlocks)
77+
.unwrap()
78+
.unwrap();
79+
let block: CompressedBlock = postcard::from_bytes(raw_block.as_slice()).unwrap();
80+
let tx_ids = block.transactions().into_iter();
81+
let raw_txs = database
82+
.multi_get(Column::Transactions.id(), tx_ids)
83+
.unwrap();
84+
for raw_tx in raw_txs {
85+
if let Some(tx) = raw_tx {
86+
let _: Transaction = postcard::from_bytes(tx.as_slice()).unwrap();
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)