406 lines
13 KiB
Rust
406 lines
13 KiB
Rust
//! GHOSTDAG benchmarks.
|
|
//!
|
|
//! Benchmarks the core GHOSTDAG operations:
|
|
//! - Block insertion with GHOSTDAG data calculation
|
|
//! - Blue score computation
|
|
//! - Merge set partitioning
|
|
//! - Selected chain traversal
|
|
//! - Reachability queries
|
|
//!
|
|
//! Run with: cargo bench -p synor-dag
|
|
|
|
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
|
use std::sync::Arc;
|
|
use synor_dag::{BlockDag, BlockId, GhostdagManager, ReachabilityStore, GHOSTDAG_K};
|
|
use synor_types::Hash256;
|
|
|
|
/// Helper to create deterministic block IDs.
|
|
fn make_block_id(n: u64) -> BlockId {
|
|
let mut bytes = [0u8; 32];
|
|
bytes[..8].copy_from_slice(&n.to_le_bytes());
|
|
Hash256::from_bytes(bytes)
|
|
}
|
|
|
|
/// Creates a test DAG setup.
|
|
fn setup_dag() -> (Arc<BlockDag>, Arc<ReachabilityStore>, GhostdagManager) {
|
|
let genesis = make_block_id(0);
|
|
let dag = Arc::new(BlockDag::new(genesis, 0));
|
|
let reachability = Arc::new(ReachabilityStore::new(genesis));
|
|
let ghostdag = GhostdagManager::new(dag.clone(), reachability.clone());
|
|
(dag, reachability, ghostdag)
|
|
}
|
|
|
|
/// Builds a linear chain of n blocks.
|
|
fn build_linear_chain(
|
|
n: usize,
|
|
dag: &Arc<BlockDag>,
|
|
reachability: &Arc<ReachabilityStore>,
|
|
ghostdag: &GhostdagManager,
|
|
) -> Vec<BlockId> {
|
|
let genesis = dag.genesis();
|
|
let mut blocks = vec![genesis];
|
|
|
|
for i in 1..=n {
|
|
let block = make_block_id(i as u64);
|
|
let parent = blocks[i - 1];
|
|
|
|
dag.insert_block(block, vec![parent], i as u64 * 100)
|
|
.unwrap();
|
|
reachability.add_block(block, parent, &[parent]).unwrap();
|
|
ghostdag.add_block(block, &[parent]).unwrap();
|
|
blocks.push(block);
|
|
}
|
|
|
|
blocks
|
|
}
|
|
|
|
/// Builds a DAG with specified width (parallel chains).
|
|
fn build_wide_dag(
|
|
depth: usize,
|
|
width: usize,
|
|
dag: &Arc<BlockDag>,
|
|
reachability: &Arc<ReachabilityStore>,
|
|
ghostdag: &GhostdagManager,
|
|
) -> Vec<Vec<BlockId>> {
|
|
let genesis = dag.genesis();
|
|
let mut layers: Vec<Vec<BlockId>> = vec![vec![genesis]];
|
|
|
|
for d in 1..=depth {
|
|
let mut layer = Vec::new();
|
|
|
|
for w in 0..width {
|
|
let block_num = (d * 1000 + w) as u64;
|
|
let block = make_block_id(block_num);
|
|
|
|
// Connect to all blocks in previous layer (up to MAX_BLOCK_PARENTS)
|
|
let parents: Vec<_> = layers[d - 1].iter().copied().collect();
|
|
let selected_parent = parents[0];
|
|
|
|
dag.insert_block(block, parents.clone(), block_num * 100)
|
|
.unwrap();
|
|
reachability
|
|
.add_block(block, selected_parent, &parents)
|
|
.unwrap();
|
|
ghostdag.add_block(block, &parents).unwrap();
|
|
|
|
layer.push(block);
|
|
}
|
|
|
|
layers.push(layer);
|
|
}
|
|
|
|
layers
|
|
}
|
|
|
|
// ==================== Block Insertion Benchmarks ====================
|
|
|
|
fn block_insertion_linear(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("ghostdag_insert_linear");
|
|
|
|
for chain_len in [10, 50, 100, 500] {
|
|
group.throughput(Throughput::Elements(1));
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(chain_len),
|
|
&chain_len,
|
|
|b, &n| {
|
|
b.iter_batched(
|
|
|| {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
// Pre-build the chain except last block
|
|
let blocks = build_linear_chain(n - 1, &dag, &reachability, &ghostdag);
|
|
(dag, reachability, ghostdag, blocks)
|
|
},
|
|
|(dag, reachability, ghostdag, blocks)| {
|
|
// Benchmark inserting the final block
|
|
let new_block = make_block_id(n as u64);
|
|
let parent = *blocks.last().unwrap();
|
|
|
|
dag.insert_block(new_block, vec![parent], n as u64 * 100)
|
|
.unwrap();
|
|
reachability
|
|
.add_block(new_block, parent, &[parent])
|
|
.unwrap();
|
|
black_box(ghostdag.add_block(new_block, &[parent]).unwrap())
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
)
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn block_insertion_wide(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("ghostdag_insert_wide");
|
|
|
|
// Test with different DAG widths
|
|
for width in [2, 4, 8, 16] {
|
|
group.throughput(Throughput::Elements(1));
|
|
group.bench_with_input(BenchmarkId::from_parameter(width), &width, |b, &w| {
|
|
b.iter_batched(
|
|
|| {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
// Pre-build DAG with specified width
|
|
let layers = build_wide_dag(10, w, &dag, &reachability, &ghostdag);
|
|
(dag, reachability, ghostdag, layers)
|
|
},
|
|
|(dag, reachability, ghostdag, layers)| {
|
|
// Benchmark inserting a new block on top
|
|
let new_block = make_block_id(99999);
|
|
let parents: Vec<_> = layers.last().unwrap().iter().copied().collect();
|
|
let selected_parent = parents[0];
|
|
|
|
dag.insert_block(new_block, parents.clone(), 99999 * 100)
|
|
.unwrap();
|
|
reachability
|
|
.add_block(new_block, selected_parent, &parents)
|
|
.unwrap();
|
|
black_box(ghostdag.add_block(new_block, &parents).unwrap())
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
)
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ==================== Blue Score Benchmarks ====================
|
|
|
|
fn blue_score_calculation(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
|
|
let tip = *blocks.last().unwrap();
|
|
|
|
c.bench_function("blue_score_get", |b| {
|
|
b.iter(|| black_box(ghostdag.get_blue_score(&tip).unwrap()))
|
|
});
|
|
}
|
|
|
|
fn blue_score_batch(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
|
|
|
|
c.bench_function("blue_score_batch_100", |b| {
|
|
b.iter(|| {
|
|
for block in &blocks {
|
|
black_box(ghostdag.get_blue_score(block).unwrap());
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
// ==================== Selected Chain Benchmarks ====================
|
|
|
|
fn selected_chain_traversal(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("selected_chain");
|
|
|
|
for chain_len in [10, 50, 100, 500] {
|
|
group.bench_with_input(
|
|
BenchmarkId::from_parameter(chain_len),
|
|
&chain_len,
|
|
|b, &n| {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(n, &dag, &reachability, &ghostdag);
|
|
let tip = *blocks.last().unwrap();
|
|
|
|
b.iter(|| black_box(ghostdag.get_selected_chain(&tip).unwrap()))
|
|
},
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ==================== Reachability Benchmarks ====================
|
|
|
|
fn reachability_ancestor_check(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
|
|
let tip = *blocks.last().unwrap();
|
|
let genesis = dag.genesis();
|
|
|
|
c.bench_function("reachability_is_ancestor_100", |b| {
|
|
b.iter(|| black_box(reachability.is_ancestor(&genesis, &tip).unwrap()))
|
|
});
|
|
}
|
|
|
|
fn reachability_anticone_check(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
|
|
// Build a forked DAG
|
|
let genesis = dag.genesis();
|
|
|
|
// First chain
|
|
let block1 = make_block_id(1);
|
|
let block2 = make_block_id(2);
|
|
dag.insert_block(block1, vec![genesis], 100).unwrap();
|
|
dag.insert_block(block2, vec![block1], 200).unwrap();
|
|
reachability.add_block(block1, genesis, &[genesis]).unwrap();
|
|
reachability.add_block(block2, block1, &[block1]).unwrap();
|
|
ghostdag.add_block(block1, &[genesis]).unwrap();
|
|
ghostdag.add_block(block2, &[block1]).unwrap();
|
|
|
|
// Second chain (parallel)
|
|
let block3 = make_block_id(3);
|
|
let block4 = make_block_id(4);
|
|
dag.insert_block(block3, vec![genesis], 150).unwrap();
|
|
dag.insert_block(block4, vec![block3], 250).unwrap();
|
|
reachability.add_block(block3, genesis, &[genesis]).unwrap();
|
|
reachability.add_block(block4, block3, &[block3]).unwrap();
|
|
ghostdag.add_block(block3, &[genesis]).unwrap();
|
|
ghostdag.add_block(block4, &[block3]).unwrap();
|
|
|
|
// block2 and block4 are in each other's anticone
|
|
c.bench_function("reachability_is_anticone", |b| {
|
|
b.iter(|| black_box(reachability.is_anticone(&block2, &block4).unwrap()))
|
|
});
|
|
}
|
|
|
|
// ==================== Merge Set Benchmarks ====================
|
|
|
|
fn merge_set_calculation(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("merge_set");
|
|
|
|
for width in [2, 4, 8] {
|
|
group.bench_with_input(BenchmarkId::from_parameter(width), &width, |b, &w| {
|
|
b.iter_batched(
|
|
|| {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let layers = build_wide_dag(5, w, &dag, &reachability, &ghostdag);
|
|
(dag, reachability, ghostdag, layers)
|
|
},
|
|
|(dag, reachability, ghostdag, layers)| {
|
|
// Insert a new block that merges multiple parents
|
|
let new_block = make_block_id(99999);
|
|
let parents: Vec<_> = layers.last().unwrap().iter().copied().collect();
|
|
let selected_parent = parents[0];
|
|
|
|
dag.insert_block(new_block, parents.clone(), 99999 * 100)
|
|
.unwrap();
|
|
reachability
|
|
.add_block(new_block, selected_parent, &parents)
|
|
.unwrap();
|
|
let data = ghostdag.add_block(new_block, &parents).unwrap();
|
|
black_box(data.merge_set_size())
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
)
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ==================== k-Cluster Benchmarks ====================
|
|
|
|
fn k_cluster_validation(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("k_cluster");
|
|
|
|
for k in [3, 10, 18] {
|
|
group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k_val| {
|
|
b.iter_batched(
|
|
|| {
|
|
let genesis = make_block_id(0);
|
|
let dag = Arc::new(BlockDag::new(genesis, 0));
|
|
let reachability = Arc::new(ReachabilityStore::new(genesis));
|
|
let ghostdag =
|
|
GhostdagManager::with_k(dag.clone(), reachability.clone(), k_val);
|
|
build_wide_dag(5, k_val as usize, &dag, &reachability, &ghostdag);
|
|
(dag, reachability, ghostdag)
|
|
},
|
|
|(dag, reachability, ghostdag)| {
|
|
// Check k-cluster property
|
|
black_box(ghostdag.k())
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
)
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ==================== GHOSTDAG Data Access ====================
|
|
|
|
fn ghostdag_data_access(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
|
|
let tip = *blocks.last().unwrap();
|
|
|
|
c.bench_function("ghostdag_get_data", |b| {
|
|
b.iter(|| black_box(ghostdag.get_data(&tip).unwrap()))
|
|
});
|
|
|
|
c.bench_function("ghostdag_get_selected_parent", |b| {
|
|
b.iter(|| black_box(ghostdag.get_selected_parent(&tip).unwrap()))
|
|
});
|
|
|
|
c.bench_function("ghostdag_is_blue", |b| {
|
|
b.iter(|| black_box(ghostdag.is_blue(&tip)))
|
|
});
|
|
}
|
|
|
|
// ==================== DAG Operations ====================
|
|
|
|
fn dag_block_lookup(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
let blocks = build_linear_chain(100, &dag, &reachability, &ghostdag);
|
|
let tip = *blocks.last().unwrap();
|
|
|
|
c.bench_function("dag_get_block", |b| {
|
|
b.iter(|| black_box(dag.get_block(&tip)))
|
|
});
|
|
|
|
c.bench_function("dag_get_parents", |b| {
|
|
b.iter(|| black_box(dag.get_parents(&tip)))
|
|
});
|
|
|
|
c.bench_function("dag_get_children", |b| {
|
|
b.iter(|| black_box(dag.get_children(&tip)))
|
|
});
|
|
}
|
|
|
|
fn dag_tips_query(c: &mut Criterion) {
|
|
let (dag, reachability, ghostdag) = setup_dag();
|
|
build_wide_dag(10, 4, &dag, &reachability, &ghostdag);
|
|
|
|
c.bench_function("dag_get_tips", |b| b.iter(|| black_box(dag.tips())));
|
|
}
|
|
|
|
// ==================== Criterion Groups ====================
|
|
|
|
criterion_group!(
|
|
insertion_benches,
|
|
block_insertion_linear,
|
|
block_insertion_wide,
|
|
);
|
|
|
|
criterion_group!(score_benches, blue_score_calculation, blue_score_batch,);
|
|
|
|
criterion_group!(chain_benches, selected_chain_traversal,);
|
|
|
|
criterion_group!(
|
|
reachability_benches,
|
|
reachability_ancestor_check,
|
|
reachability_anticone_check,
|
|
);
|
|
|
|
criterion_group!(merge_benches, merge_set_calculation, k_cluster_validation,);
|
|
|
|
criterion_group!(
|
|
data_access_benches,
|
|
ghostdag_data_access,
|
|
dag_block_lookup,
|
|
dag_tips_query,
|
|
);
|
|
|
|
criterion_main!(
|
|
insertion_benches,
|
|
score_benches,
|
|
chain_benches,
|
|
reachability_benches,
|
|
merge_benches,
|
|
data_access_benches
|
|
);
|