//! 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, Arc, 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, reachability: &Arc, ghostdag: &GhostdagManager, ) -> Vec { 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, reachability: &Arc, ghostdag: &GhostdagManager, ) -> Vec> { let genesis = dag.genesis(); let mut layers: Vec> = 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 );