From 059d0e610507f2c6ec9b1faa528026a2f3599661 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 17:06:28 -0400 Subject: [PATCH 1/4] add `benches/criterion/cuprate-blockchain` --- Cargo.lock | 15 +++++ Cargo.toml | 1 + .../criterion/cuprate-blockchain/Cargo.toml | 28 ++++++++ .../cuprate-blockchain/benches/block.rs | 67 +++++++++++++++++++ .../cuprate-blockchain/benches/main.rs | 7 ++ .../cuprate-blockchain/src/blocks.rs | 36 ++++++++++ .../criterion/cuprate-blockchain/src/lib.rs | 7 ++ .../cuprate-blockchain/src/tmp_env.rs | 53 +++++++++++++++ 8 files changed, 214 insertions(+) create mode 100644 benches/criterion/cuprate-blockchain/Cargo.toml create mode 100644 benches/criterion/cuprate-blockchain/benches/block.rs create mode 100644 benches/criterion/cuprate-blockchain/benches/main.rs create mode 100644 benches/criterion/cuprate-blockchain/src/blocks.rs create mode 100644 benches/criterion/cuprate-blockchain/src/lib.rs create mode 100644 benches/criterion/cuprate-blockchain/src/tmp_env.rs diff --git a/Cargo.lock b/Cargo.lock index 2555b9b..41d5bfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -758,6 +758,21 @@ dependencies = [ name = "cuprate-constants" version = "0.1.0" +[[package]] +name = "cuprate-criterion-blockchain" +version = "0.0.0" +dependencies = [ + "criterion", + "cuprate-blockchain", + "cuprate-helper", + "cuprate-test-utils", + "cuprate-types", + "function_name", + "rand", + "serde_json", + "tempfile", +] + [[package]] name = "cuprate-criterion-example" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 6f2cb3f..ef41942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "benches/benchmark/example", "benches/criterion/example", "benches/criterion/cuprate-json-rpc", + "benches/criterion/cuprate-blockchain", # Consensus "consensus", "consensus/context", diff --git a/benches/criterion/cuprate-blockchain/Cargo.toml b/benches/criterion/cuprate-blockchain/Cargo.toml new file mode 100644 index 0000000..de3dab1 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "cuprate-criterion-blockchain" +version = "0.0.0" +edition = "2021" +description = "Criterion benchmarking for cuprate-blockchain" +license = "MIT" +authors = ["hinto-janai"] +repository = "https://github.com/Cuprate/cuprate/tree/main/benches/criterion/cuprate-blockchain" +keywords = ["cuprate", "blockchain", "criterion", "benchmark"] + +[dependencies] +cuprate-blockchain = { path = "../../../storage/blockchain" } +cuprate-test-utils = { path = "../../../test-utils" } +cuprate-types = { path = "../../../types", default-features = false } +cuprate-helper = { path = "../../../helper", features = ["cast"] } + +criterion = { workspace = true } +function_name = { workspace = true } +serde_json = { workspace = true, features = ["default"] } +tempfile = { workspace = true } +rand = { workspace = true } + +[[bench]] +name = "main" +harness = false + +[lints] +workspace = true diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs new file mode 100644 index 0000000..2a3c55a --- /dev/null +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -0,0 +1,67 @@ +//! Benchmarks for [`block`] functions. + +#![allow(unused_attributes, unused_crate_dependencies)] + +use std::time::Instant; + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use function_name::named; + +use cuprate_blockchain::{ + cuprate_database::{Env, EnvInner}, + ops::block, + tables::OpenTables, +}; +use cuprate_test_utils::data::{BLOCK_V16_TX0, BLOCK_V1_TX2, BLOCK_V9_TX3}; +use cuprate_types::VerifiedBlockInformation; + +use cuprate_criterion_blockchain::generate_fake_blocks; + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = + add_block_v1_tx2, + add_block_v9_tx3, + add_block_v16_tx0, +} +criterion_main!(benches); + +/// Inner function for benchmarking [`block::add_block`]. +#[expect(clippy::significant_drop_tightening)] +fn add_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { + let env = cuprate_criterion_blockchain::TmpEnv::new(); + + c.bench_function(function_name, |b| { + // We use `iter_custom` because we need to generate an + // appropriate amount of blocks and only time the `add_block`. + b.iter_custom(|count| { + let blocks = black_box(generate_fake_blocks(block, count)); + + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let start = Instant::now(); + for block in &blocks { + black_box(block::add_block(block, &mut tables)).unwrap(); + } + start.elapsed() + }); + }); +} + +#[named] +fn add_block_v1_tx2(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V1_TX2); +} + +#[named] +fn add_block_v9_tx3(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V9_TX3); +} + +#[named] +fn add_block_v16_tx0(c: &mut Criterion) { + add_block_inner(c, function_name!(), &BLOCK_V16_TX0); +} diff --git a/benches/criterion/cuprate-blockchain/benches/main.rs b/benches/criterion/cuprate-blockchain/benches/main.rs new file mode 100644 index 0000000..daa06c1 --- /dev/null +++ b/benches/criterion/cuprate-blockchain/benches/main.rs @@ -0,0 +1,7 @@ +#![allow(unused_crate_dependencies)] + +mod block; + +criterion::criterion_main! { + block::benches, +} diff --git a/benches/criterion/cuprate-blockchain/src/blocks.rs b/benches/criterion/cuprate-blockchain/src/blocks.rs new file mode 100644 index 0000000..a5629af --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/blocks.rs @@ -0,0 +1,36 @@ +use rand::Rng; + +use cuprate_helper::cast::u64_to_usize; +use cuprate_types::VerifiedBlockInformation; + +/// Generate fake [`VerifiedBlockInformation`]s. +/// +/// This function generates fake blocks, +/// cloning the `base` block `count` amount of times, +/// starting at height `0` and sequentially incrementing, +/// i.e. block height `{0,1,2,...}`. +/// +/// Each block has a random [`VerifiedBlockInformation::block_hash`]. +/// +/// # Hack +/// This is used for these benchmarks because +/// [`cuprate_blockchain::ops::block::add_block`] +/// asserts that blocks with non-sequential heights cannot be added. +/// To get around this, we manually edit the block heights. +/// +/// The hash must also be faked as +/// [`cuprate_blockchain::ops::blockchain::chain_height`] +/// which is used for an [`assert`] relies on the `hash -> height` table. +pub fn generate_fake_blocks( + base: &VerifiedBlockInformation, + count: u64, +) -> Vec { + (0..count) + .map(|height| { + let mut block = base.clone(); + block.height = u64_to_usize(height); + block.block_hash = rand::thread_rng().r#gen(); + block + }) + .collect() +} diff --git a/benches/criterion/cuprate-blockchain/src/lib.rs b/benches/criterion/cuprate-blockchain/src/lib.rs new file mode 100644 index 0000000..59f417e --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/lib.rs @@ -0,0 +1,7 @@ +#![allow(unused_crate_dependencies, reason = "used in benchmarks")] + +mod tmp_env; +mod blocks; + +pub use blocks::generate_fake_blocks; +pub use tmp_env::TmpEnv; diff --git a/benches/criterion/cuprate-blockchain/src/tmp_env.rs b/benches/criterion/cuprate-blockchain/src/tmp_env.rs new file mode 100644 index 0000000..147705d --- /dev/null +++ b/benches/criterion/cuprate-blockchain/src/tmp_env.rs @@ -0,0 +1,53 @@ +//! An [`Env`] inside a [`TempDir`]. + +use tempfile::TempDir; + +use cuprate_blockchain::{ + config::ReaderThreads, + cuprate_database::{config::ConfigBuilder, resize::PAGE_SIZE, ConcreteEnv, Env}, +}; + +/// A temporary in-memory [`Env`]. +/// +/// This is a [`ConcreteEnv`] that uses [`TempDir`] as the +/// backing file location - this is an in-memory file on Linux. +pub struct TmpEnv { + pub env: ConcreteEnv, + pub tempdir: TempDir, +} + +impl Default for TmpEnv { + fn default() -> Self { + Self::new() + } +} + +impl TmpEnv { + /// Create an `Env` in a temporary directory. + /// + /// The directory is automatically removed after the [`TempDir`] is dropped. + #[expect(clippy::missing_panics_doc)] + pub fn new() -> Self { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().to_path_buf().into(); + let db_config = ConfigBuilder::new(path).low_power().build(); + let reader_threads = ReaderThreads::One; + let config = cuprate_blockchain::config::Config { + db_config, + reader_threads, + }; + let env = cuprate_blockchain::open(config).unwrap(); + + // Resize to a very large map to prevent resize errors. + if ConcreteEnv::MANUAL_RESIZE { + // SAFETY: no write transactions exist yet. + unsafe { + env.env_inner() + .resize(PAGE_SIZE.get() * 1024 * 1024 * 1024) + .unwrap(); + } + } + + Self { env, tempdir } + } +} From e5db01a2e3515a3022e6812651cc50941476c7d5 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 17:09:41 -0400 Subject: [PATCH 2/4] fmt --- benches/criterion/cuprate-blockchain/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/criterion/cuprate-blockchain/src/lib.rs b/benches/criterion/cuprate-blockchain/src/lib.rs index 59f417e..8687f45 100644 --- a/benches/criterion/cuprate-blockchain/src/lib.rs +++ b/benches/criterion/cuprate-blockchain/src/lib.rs @@ -1,7 +1,7 @@ #![allow(unused_crate_dependencies, reason = "used in benchmarks")] -mod tmp_env; mod blocks; +mod tmp_env; pub use blocks::generate_fake_blocks; pub use tmp_env::TmpEnv; From 1cf86ed5c03d9d9ff0dd2203ff9817846a570f73 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 19:44:09 -0400 Subject: [PATCH 3/4] alt_block --- .../cuprate-blockchain/benches/block.rs | 67 +++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs index 2a3c55a..a03bf18 100644 --- a/benches/criterion/cuprate-blockchain/benches/block.rs +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -1,19 +1,20 @@ -//! Benchmarks for [`block`] functions. +//! Benchmarks for [`block`] and [`alt_block`] functions. #![allow(unused_attributes, unused_crate_dependencies)] -use std::time::Instant; +use std::{num::NonZeroU64, time::Instant}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use cuprate_helper::cast::usize_to_u64; use function_name::named; use cuprate_blockchain::{ cuprate_database::{Env, EnvInner}, - ops::block, + ops::{alt_block, block}, tables::OpenTables, }; use cuprate_test_utils::data::{BLOCK_V16_TX0, BLOCK_V1_TX2, BLOCK_V9_TX3}; -use cuprate_types::VerifiedBlockInformation; +use cuprate_types::{AltBlockInformation, ChainId, VerifiedBlockInformation}; use cuprate_criterion_blockchain::generate_fake_blocks; @@ -24,6 +25,9 @@ criterion_group! { add_block_v1_tx2, add_block_v9_tx3, add_block_v16_tx0, + add_alt_block_v1_tx2, + add_alt_block_v9_tx3, + add_alt_block_v16_tx0, } criterion_main!(benches); @@ -65,3 +69,58 @@ fn add_block_v9_tx3(c: &mut Criterion) { fn add_block_v16_tx0(c: &mut Criterion) { add_block_inner(c, function_name!(), &BLOCK_V16_TX0); } + +/// Inner function for benchmarking [`alt_block::add_alt_block`]. +#[expect(clippy::significant_drop_tightening)] +fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { + let env = cuprate_criterion_blockchain::TmpEnv::new(); + + c.bench_function(function_name, |b| { + // We use `iter_custom` because we need to generate an + // appropriate amount of blocks and only time the `add_block`. + b.iter_custom(|count| { + // Map the block to a fake alt block. + let blocks = generate_fake_blocks(block, count) + .into_iter() + .enumerate() + .map(|(i, b)| AltBlockInformation { + block: b.block, + block_blob: b.block_blob, + txs: b.txs, + block_hash: b.block_hash, + pow_hash: b.pow_hash, + height: b.height, + weight: b.weight, + long_term_weight: b.long_term_weight, + cumulative_difficulty: b.cumulative_difficulty, + chain_id: ChainId(NonZeroU64::new(usize_to_u64(i) + 1).unwrap()), + }) + .collect::>(); + + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let start = Instant::now(); + for block in &blocks { + black_box(alt_block::add_alt_block(block, &mut tables)).unwrap(); + } + start.elapsed() + }); + }); +} + +#[named] +fn add_alt_block_v1_tx2(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V1_TX2); +} + +#[named] +fn add_alt_block_v9_tx3(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V9_TX3); +} + +#[named] +fn add_alt_block_v16_tx0(c: &mut Criterion) { + add_alt_block_inner(c, function_name!(), &BLOCK_V16_TX0); +} From f65ff1bb0ec9833d5215d139d5bb3da2a35b2849 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 31 Oct 2024 19:53:36 -0400 Subject: [PATCH 4/4] fix alt_block height --- .../cuprate-blockchain/benches/block.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/benches/criterion/cuprate-blockchain/benches/block.rs b/benches/criterion/cuprate-blockchain/benches/block.rs index a03bf18..4a2bd88 100644 --- a/benches/criterion/cuprate-blockchain/benches/block.rs +++ b/benches/criterion/cuprate-blockchain/benches/block.rs @@ -48,6 +48,7 @@ fn add_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlock let start = Instant::now(); for block in &blocks { + let block = black_box(block); black_box(block::add_block(block, &mut tables)).unwrap(); } start.elapsed() @@ -75,6 +76,18 @@ fn add_block_v16_tx0(c: &mut Criterion) { fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedBlockInformation) { let env = cuprate_criterion_blockchain::TmpEnv::new(); + // We must have at least 1 block or else `add_alt_block` will panic. + { + let env_inner = env.env.env_inner(); + let tx_rw = env_inner.tx_rw().unwrap(); + let mut tables = env_inner.open_tables_mut(&tx_rw).unwrap(); + + let mut block = BLOCK_V1_TX2.clone(); + block.height = 0; + + block::add_block(&block, &mut tables).unwrap(); + } + c.bench_function(function_name, |b| { // We use `iter_custom` because we need to generate an // appropriate amount of blocks and only time the `add_block`. @@ -89,7 +102,7 @@ fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedB txs: b.txs, block_hash: b.block_hash, pow_hash: b.pow_hash, - height: b.height, + height: b.height + 1, weight: b.weight, long_term_weight: b.long_term_weight, cumulative_difficulty: b.cumulative_difficulty, @@ -103,6 +116,7 @@ fn add_alt_block_inner(c: &mut Criterion, function_name: &str, block: &VerifiedB let start = Instant::now(); for block in &blocks { + let block = black_box(block); black_box(alt_block::add_alt_block(block, &mut tables)).unwrap(); } start.elapsed()