use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use integration_tests_sv2::mining_device::{set_nonces_per_call, FastSha256d};
use rand::{thread_rng, Rng};
use std::time::Duration;
use stratum_apps::stratum_core::bitcoin::{
block::Version, blockdata::block::Header, hash_types::BlockHash, hashes::Hash, CompactTarget,
};
fn random_header() -> Header {
let mut rng = thread_rng();
let prev_hash: [u8; 32] = rng.gen();
let prev_hash = Hash::from_byte_array(prev_hash);
let merkle_root: [u8; 32] = rng.gen();
let merkle_root = Hash::from_byte_array(merkle_root);
Header {
version: Version::from_consensus(rng.gen::<i32>()),
prev_blockhash: BlockHash::from_raw_hash(prev_hash),
merkle_root,
time: std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH - std::time::Duration::from_secs(60))
.unwrap()
.as_secs() as u32,
bits: CompactTarget::from_consensus(rng.gen()),
nonce: 0,
}
}
fn bench_microbatch(c: &mut Criterion) {
#[cfg(target_arch = "x86_64")]
println!(
"Hardware SHA available (x86 SHA-NI): {}",
std::is_x86_feature_detected!("sha")
);
#[cfg(target_arch = "aarch64")]
println!(
"Hardware SHA available (ARMv8 SHA2): {}",
std::arch::is_aarch64_feature_detected!("sha2")
);
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
println!("Hardware SHA detection: not applicable for this arch");
let mut group = c.benchmark_group("mining_device_microbatch");
group.sample_size(10);
group.warm_up_time(Duration::from_millis(100));
group.measurement_time(Duration::from_secs(1));
let header = random_header();
let mut fast = FastSha256d::from_header_static(&header);
let batches: Vec<u32> = std::env::var("MINING_DEVICE_BATCH_SIZES")
.ok()
.and_then(|s| {
s.split(',')
.map(|p| p.trim().parse::<u32>().ok())
.collect::<Option<Vec<u32>>>()
})
.filter(|v| !v.is_empty())
.unwrap_or_else(|| vec![1, 8, 32, 128]);
for &b in &batches {
group.throughput(Throughput::Elements(b as u64));
group.bench_function(BenchmarkId::from_parameter(b), |bencher| {
set_nonces_per_call(b);
let mut h = header;
bencher.iter(|| {
let start = h.nonce;
let time = h.time;
for i in 0..b {
let hsh = fast.hash_with_nonce_time(start.wrapping_add(i), time);
black_box(hsh);
}
h.nonce = start.wrapping_add(b);
});
});
let mut h = header;
set_nonces_per_call(b);
let reps: u32 = 200_000 / b.max(1); let total_hashes: u64 = reps as u64 * b as u64;
let start_inst = std::time::Instant::now();
for _ in 0..reps {
let start = h.nonce;
let time = h.time;
for i in 0..b {
let _ = black_box(fast.hash_with_nonce_time(start.wrapping_add(i), time));
}
h.nonce = start.wrapping_add(b);
}
let dur = start_inst.elapsed();
let secs = dur.as_secs_f64().max(1e-9);
let hps = (total_hashes as f64) / secs; let mhps = hps / 1_000_000.0;
println!("batch={b}: ~{mhps:.3} MH/s");
}
group.finish();
}
criterion_group!(benches, bench_microbatch);
criterion_main!(benches);