synor/crates/synor-mining/benches/mining_bench.rs
Gulshan Yadav 5c643af64c fix: resolve all clippy warnings for CI
Fix all Rust clippy warnings that were causing CI failures when built
with RUSTFLAGS=-Dwarnings. Changes include:

- Replace derivable_impls with derive macros for BlockBody, Network, etc.
- Use div_ceil() instead of manual implementation
- Fix should_implement_trait by renaming from_str to parse
- Add type aliases for type_complexity warnings
- Use or_default(), is_some_and(), is_multiple_of() where appropriate
- Remove needless borrows and redundant closures
- Fix manual_strip with strip_prefix()
- Add allow attributes for intentional patterns (too_many_arguments,
  needless_range_loop in cryptographic code, assertions_on_constants)
- Remove unused imports, mut bindings, and dead code in tests
2026-01-08 05:58:22 +05:30

606 lines
17 KiB
Rust

//! Mining benchmarks for Synor blockchain.
//!
//! Benchmarks:
//! - kHeavyHash hashing throughput
//! - Matrix initialization
//! - Nonce search rate
//! - Block template creation
//! - Mining coordination
//!
//! Run with: cargo bench -p synor-mining --bench mining_bench
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use synor_mining::{
kheavyhash::{KHeavyHash, ParallelMiner},
matrix::{GfTables, HeavyMatrix, OptimizedMatrix},
miner::{BlockMiner, MinerConfig, ParallelBlockMiner},
template::{
BlockTemplate, BlockTemplateBuilder, CoinbaseBuilder, CoinbaseData, TemplateTransaction,
},
Target,
};
use synor_types::{Address, Hash256, Network};
// ==================== Test Data Helpers ====================
/// Creates a test address.
fn test_address() -> Address {
Address::from_ed25519_pubkey(Network::Mainnet, &[0x42; 32])
}
/// Creates a deterministic hash.
fn test_hash(n: u8) -> Hash256 {
let mut bytes = [0u8; 32];
bytes[0] = n;
bytes[31] = n;
Hash256::from_bytes(bytes)
}
/// Creates a test coinbase.
fn test_coinbase(height: u64) -> CoinbaseData {
CoinbaseBuilder::new(test_address(), height)
.extra_data(b"Synor Benchmark Pool".to_vec())
.reward(500_00000000)
.fees(1_00000000)
.build()
}
/// Creates a template transaction.
fn test_template_tx(n: u64) -> TemplateTransaction {
TemplateTransaction {
txid: test_hash((n % 256) as u8),
data: vec![0x42u8; 250], // ~250 byte transaction
fee: 1000 + (n * 100),
mass: 1000 + (n * 50),
}
}
/// Creates a block template with the specified number of transactions.
fn create_test_template(tx_count: usize) -> BlockTemplate {
let mut builder = BlockTemplateBuilder::new()
.version(1)
.add_parent(test_hash(1))
.add_parent(test_hash(2))
.selected_parent(test_hash(1))
.timestamp(1234567890000)
.bits(0x207fffff) // Easy target for benchmarking
.blue_score(1000)
.coinbase(test_coinbase(1000))
.reward(500_00000000);
for i in 0..tx_count {
builder = builder.add_transaction(test_template_tx(i as u64));
}
builder.build(1).unwrap()
}
// ==================== kHeavyHash Throughput ====================
fn kheavyhash_throughput_single(c: &mut Criterion) {
let hasher = KHeavyHash::new();
let header = [0x42u8; 80];
let pre_hash = hasher.pre_hash(&header);
let mut group = c.benchmark_group("kheavyhash_throughput");
group.throughput(Throughput::Elements(1));
group.bench_function("single_hash", |b| {
let mut nonce = 0u64;
b.iter(|| {
let result = hasher.finalize(&pre_hash, nonce);
nonce = nonce.wrapping_add(1);
black_box(result)
})
});
// Batch throughput
for batch_size in [100, 1000, 10000] {
group.throughput(Throughput::Elements(batch_size));
group.bench_with_input(
BenchmarkId::new("batch", batch_size),
&batch_size,
|b, &size| {
b.iter(|| {
for nonce in 0..size {
black_box(hasher.finalize(&pre_hash, nonce));
}
})
},
);
}
group.finish();
}
fn kheavyhash_sustained_rate(c: &mut Criterion) {
let hasher = KHeavyHash::new();
let header = [0xAB; 80];
let pre_hash = hasher.pre_hash(&header);
c.bench_function("kheavyhash_sustained_10k", |b| {
b.iter(|| {
let mut count = 0u64;
for nonce in 0..10000 {
let pow = hasher.finalize(&pre_hash, nonce);
// Simulate target check
if pow.hash.as_bytes()[0] == 0 {
count += 1;
}
}
black_box(count)
})
});
}
// ==================== Matrix Initialization ====================
fn matrix_initialization(c: &mut Criterion) {
let mut group = c.benchmark_group("matrix_init");
group.bench_function("heavy_matrix_new", |b| {
b.iter(|| black_box(HeavyMatrix::new()))
});
group.bench_function("optimized_matrix_new", |b| {
b.iter(|| black_box(OptimizedMatrix::new()))
});
group.bench_function("gf_tables_new", |b| b.iter(|| black_box(GfTables::new())));
// Custom seed initialization
for seed_idx in [0u8, 42u8, 255u8] {
let seed = [seed_idx; 32];
group.bench_with_input(
BenchmarkId::new("matrix_from_seed", seed_idx),
&seed,
|b, s| b.iter(|| black_box(HeavyMatrix::from_seed(s))),
);
}
group.finish();
}
fn matrix_memory_patterns(c: &mut Criterion) {
let optimized = OptimizedMatrix::new();
let mut group = c.benchmark_group("matrix_patterns");
// Sequential access pattern
group.bench_function("sequential_input", |b| {
let inputs: Vec<[u8; 32]> = (0..100)
.map(|i| {
let mut arr = [0u8; 32];
for (j, byte) in arr.iter_mut().enumerate() {
*byte = ((i + j) & 0xFF) as u8;
}
arr
})
.collect();
b.iter(|| {
for input in &inputs {
black_box(optimized.multiply(input));
}
})
});
// Random access pattern
group.bench_function("random_input", |b| {
// Pre-generated "random" inputs
let inputs: Vec<[u8; 32]> = (0..100)
.map(|i| {
let mut arr = [0u8; 32];
let mut state = i as u8;
for byte in arr.iter_mut() {
state = state.wrapping_mul(17).wrapping_add(31);
*byte = state;
}
arr
})
.collect();
b.iter(|| {
for input in &inputs {
black_box(optimized.multiply(input));
}
})
});
group.finish();
}
// ==================== Nonce Search Rate ====================
fn nonce_search_rate(c: &mut Criterion) {
let hasher = KHeavyHash::new();
let header = [0x42u8; 80];
let mut group = c.benchmark_group("nonce_search");
// Search with very easy target (always finds quickly)
group.bench_function("easy_target_search", |b| {
let target = Target::max();
b.iter(|| black_box(hasher.mine(&header, &target, 0, 1000)))
});
// Search through fixed ranges
for range in [1000u64, 5000, 10000] {
let target = Target::from_bytes([0x00; 32]); // Impossible target
group.throughput(Throughput::Elements(range));
group.bench_with_input(BenchmarkId::new("range_search", range), &range, |b, &r| {
b.iter(|| black_box(hasher.mine(&header, &target, 0, r)))
});
}
// Search with progress callback
group.bench_function("search_with_callback", |b| {
let target = Target::max();
b.iter(|| hasher.mine_with_callback(&header, &target, 0, 5000, |_tried, _nonce| true))
});
group.finish();
}
fn parallel_nonce_search(c: &mut Criterion) {
let mut group = c.benchmark_group("parallel_nonce_search");
let header = [0x42u8; 80];
let target = Target::max();
for threads in [1, 2, 4] {
group.bench_with_input(BenchmarkId::new("threads", threads), &threads, |b, &t| {
let miner = ParallelMiner::new(t);
b.iter(|| black_box(miner.mine(&header, &target, 0)))
});
}
group.finish();
}
// ==================== Block Template Creation ====================
fn block_template_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("template_creation");
for tx_count in [0, 10, 50, 100, 500] {
group.bench_with_input(
BenchmarkId::new("tx_count", tx_count),
&tx_count,
|b, &count| {
b.iter_batched(
|| {
// Setup: create transactions
let txs: Vec<TemplateTransaction> =
(0..count).map(|i| test_template_tx(i as u64)).collect();
txs
},
|txs| {
// Build template
let mut builder = BlockTemplateBuilder::new()
.version(1)
.add_parent(test_hash(1))
.timestamp(1234567890000)
.bits(0x1d00ffff)
.blue_score(1000)
.coinbase(test_coinbase(1000))
.reward(500_00000000);
for tx in txs {
builder = builder.add_transaction(tx);
}
black_box(builder.build(1))
},
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();
}
fn template_header_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("template_header");
for tx_count in [0, 50, 200] {
let template = create_test_template(tx_count);
group.bench_with_input(
BenchmarkId::new("header_for_mining", tx_count),
&template,
|b, tmpl| b.iter(|| black_box(tmpl.header_for_mining())),
);
group.bench_with_input(
BenchmarkId::new("header_with_extra_nonce", tx_count),
&template,
|b, tmpl| b.iter(|| black_box(tmpl.header_with_extra_nonce(12345))),
);
}
group.finish();
}
fn template_validation(c: &mut Criterion) {
let mut group = c.benchmark_group("template_validation");
for tx_count in [0, 50, 200] {
let template = create_test_template(tx_count);
group.bench_with_input(
BenchmarkId::new("validate", tx_count),
&template,
|b, tmpl| b.iter(|| black_box(tmpl.validate())),
);
group.bench_with_input(
BenchmarkId::new("get_target", tx_count),
&template,
|b, tmpl| b.iter(|| black_box(tmpl.get_target())),
);
}
group.finish();
}
fn coinbase_building(c: &mut Criterion) {
let mut group = c.benchmark_group("coinbase_building");
group.bench_function("coinbase_simple", |b| {
b.iter(|| black_box(CoinbaseBuilder::new(test_address(), 1000).build()))
});
group.bench_function("coinbase_with_data", |b| {
b.iter(|| {
black_box(
CoinbaseBuilder::new(test_address(), 1000)
.extra_data(b"Synor Mining Pool v1.0.0".to_vec())
.reward(500_00000000)
.fees(1_00000000)
.build(),
)
})
});
// Varying extra data sizes
for data_size in [0, 32, 100, 256] {
let extra_data = vec![0x42u8; data_size];
group.bench_with_input(
BenchmarkId::new("extra_data_size", data_size),
&extra_data,
|b, data| {
b.iter(|| {
black_box(
CoinbaseBuilder::new(test_address(), 1000)
.extra_data(data.clone())
.reward(500_00000000)
.build(),
)
})
},
);
}
group.finish();
}
// ==================== Miner Configuration ====================
fn miner_config_creation(c: &mut Criterion) {
c.bench_function("miner_config_default", |b| {
b.iter(|| black_box(MinerConfig::default()))
});
c.bench_function("miner_config_solo", |b| {
b.iter(|| black_box(MinerConfig::solo(test_address(), 4)))
});
c.bench_function("miner_config_pool", |b| {
b.iter(|| black_box(MinerConfig::pool(test_address(), 4, 12345)))
});
}
fn block_miner_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("miner_creation");
for threads in [1, 2, 4, 8] {
group.bench_with_input(
BenchmarkId::new("block_miner", threads),
&threads,
|b, &t| {
let config = MinerConfig::solo(test_address(), t);
b.iter(|| black_box(BlockMiner::new(config.clone())))
},
);
}
group.finish();
}
fn parallel_block_miner_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("parallel_miner_creation");
for threads in [1, 2, 4, 8] {
group.bench_with_input(
BenchmarkId::new("parallel_block_miner", threads),
&threads,
|b, &t| {
let config = MinerConfig::solo(test_address(), t);
b.iter(|| black_box(ParallelBlockMiner::new(config.clone())))
},
);
}
group.finish();
}
// ==================== Target Operations ====================
fn target_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("target_ops");
// Target from bits
for bits in [0x1d00ffff, 0x1a0fffff, 0x170fffff] {
group.bench_with_input(
BenchmarkId::new("from_bits", format!("{:08x}", bits)),
&bits,
|b, &bit| b.iter(|| black_box(Target::from_bits(bit))),
);
}
// Target comparison
let target = Target::from_bits(0x1d00ffff);
let easy_hash = Hash256::from_bytes([0x00; 32]);
let hard_hash = Hash256::from_bytes([0xFF; 32]);
group.bench_function("is_met_by_pass", |b| {
b.iter(|| black_box(target.is_met_by(&easy_hash)))
});
group.bench_function("is_met_by_fail", |b| {
b.iter(|| black_box(target.is_met_by(&hard_hash)))
});
// Difficulty calculation
group.bench_function("to_difficulty", |b| {
b.iter(|| black_box(target.to_difficulty()))
});
group.finish();
}
// ==================== Mining Statistics ====================
fn mining_stats_operations(c: &mut Criterion) {
use synor_mining::MiningStats;
let mut group = c.benchmark_group("mining_stats");
group.bench_function("stats_default", |b| {
b.iter(|| black_box(MiningStats::default()))
});
group.bench_function("update_hashrate", |b| {
b.iter_batched(
MiningStats::default,
|mut stats| {
stats.update_hashrate(1000000, 1000);
black_box(stats)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_function("record_block", |b| {
b.iter_batched(
|| (MiningStats::default(), 1000u64),
|(mut stats, timestamp)| {
stats.record_block(timestamp);
black_box(stats)
},
criterion::BatchSize::SmallInput,
)
});
group.bench_function("formatted_hashrate", |b| {
let mut stats = MiningStats::default();
stats.update_hashrate(1_000_000_000, 1000); // 1 GH/s
b.iter(|| black_box(stats.formatted_hashrate()))
});
group.finish();
}
// ==================== End-to-End Mining ====================
fn end_to_end_mining(c: &mut Criterion) {
let mut group = c.benchmark_group("e2e_mining");
// Full mining cycle: template -> hash -> check target
group.bench_function("full_cycle", |b| {
let template = create_test_template(50);
let hasher = KHeavyHash::new();
let target = template.get_target();
b.iter(|| {
let header = template.header_for_mining();
let pre_hash = hasher.pre_hash(&header);
let pow = hasher.finalize(&pre_hash, 12345);
black_box(target.is_met_by(&pow.hash))
})
});
// Mining with template rotation (simulating new templates arriving)
group.bench_function("template_rotation_100_hashes", |b| {
let templates: Vec<BlockTemplate> = (0..10)
.map(|i| {
let mut t = create_test_template(10);
t.id = i;
t
})
.collect();
let hasher = KHeavyHash::new();
b.iter(|| {
let mut total_hashes = 0u64;
for template in &templates {
let header = template.header_for_mining();
let pre_hash = hasher.pre_hash(&header);
for nonce in 0..10 {
let _ = hasher.finalize(&pre_hash, nonce);
total_hashes += 1;
}
}
black_box(total_hashes)
})
});
group.finish();
}
// ==================== Criterion Groups ====================
criterion_group!(
throughput_benches,
kheavyhash_throughput_single,
kheavyhash_sustained_rate,
);
criterion_group!(
matrix_benches,
matrix_initialization,
matrix_memory_patterns,
);
criterion_group!(nonce_benches, nonce_search_rate, parallel_nonce_search,);
criterion_group!(
template_benches,
block_template_creation,
template_header_creation,
template_validation,
coinbase_building,
);
criterion_group!(
miner_benches,
miner_config_creation,
block_miner_creation,
parallel_block_miner_creation,
);
criterion_group!(misc_benches, target_operations, mining_stats_operations,);
criterion_group!(e2e_benches, end_to_end_mining,);
criterion_main!(
throughput_benches,
matrix_benches,
nonce_benches,
template_benches,
miner_benches,
misc_benches,
e2e_benches
);