diff --git a/ethash/benches/basic.rs b/ethash/benches/basic.rs index 5bc10e948..64c31cb21 100644 --- a/ethash/benches/basic.rs +++ b/ethash/benches/basic.rs @@ -21,22 +21,39 @@ extern crate ethash; use criterion::Criterion; use ethash::{NodeCacheBuilder, OptimizeFor}; -const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, - 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, - 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72]; +const HASH: [u8; 32] = [ + 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, + 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, + 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, + 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72, +]; +const MIX_HASH: [u8; 32] = [ + 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, + 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, + 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, + 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d, +]; const NONCE: u64 = 0xd7b3ac70a301a249; -criterion_group!( - basic, - bench_light_compute_memmap, - bench_light_compute_memory, - bench_light_new_round_trip_memmap, - bench_light_new_round_trip_memory, - bench_light_from_file_round_trip_memory, - bench_light_from_file_round_trip_memmap -); +criterion_group! { + name = basic; + config = dont_take_an_eternity_to_run(); + targets = bench_light_compute_memmap, + bench_light_compute_memory, + bench_light_new_round_trip_memmap, + bench_light_new_round_trip_memory, + bench_light_from_file_round_trip_memory, + bench_light_from_file_round_trip_memmap, + bench_quick_get_difficulty, +} criterion_main!(basic); +fn dont_take_an_eternity_to_run() -> Criterion { + Criterion::default().nresamples(1_000) + .without_plots() + .sample_size(10) +} + fn bench_light_compute_memmap(b: &mut Criterion) { use std::env; @@ -52,13 +69,13 @@ fn bench_light_compute_memory(b: &mut Criterion) { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); + b.bench_function("bench_light_compute_memory", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); } fn bench_light_new_round_trip_memmap(b: &mut Criterion) { use std::env; - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_new_round_trip_memmap", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); light.compute(&HASH, NONCE, u64::max_value()); @@ -68,7 +85,7 @@ fn bench_light_new_round_trip_memmap(b: &mut Criterion) { fn bench_light_new_round_trip_memory(b: &mut Criterion) { use std::env; - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_new_round_trip_memory", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); light.compute(&HASH, NONCE, u64::max_value()); @@ -86,7 +103,7 @@ fn bench_light_from_file_round_trip_memory(b: &mut Criterion) { dummy.to_file().unwrap(); } - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_from_file_round_trip_memory", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light_from_file(&dir, 486382).unwrap(); light.compute(&HASH, NONCE, u64::max_value()); @@ -105,9 +122,21 @@ fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) { dummy.to_file().unwrap(); } - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_from_file_round_trip_memmap", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let light = builder.light_from_file(&dir, 486382).unwrap(); light.compute(&HASH, NONCE, u64::max_value()); })); } + +fn bench_quick_get_difficulty(b: &mut Criterion) { + b.bench_function("bench_quick_get_difficulty", move |b| b.iter(|| { + let d = ethash::quick_get_difficulty(&HASH, NONCE, &MIX_HASH, false); + let boundary_good = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, + 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, + 0xe9, 0x7e, 0x53, 0x84, + ]; + assert_eq!(d[..], boundary_good[..]); + })); +} diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 36826121d..c65e89515 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -26,7 +26,7 @@ use seed_compute::SeedHashCompute; use shared::*; use std::io; -use std::{mem, ptr}; +use std::mem; use std::path::Path; const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4; @@ -135,22 +135,16 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256, pro let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]); keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash)) } else { - // This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set - // with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write - // the next 32 bytes before we read the whole thing with `keccak_256`. - // - // This cannot be elided by the compiler as it doesn't know the implementation of - // `keccak_512`. - let mut buf: [u8; 64 + 32] = mem::uninitialized(); + let mut buf = [0u8; 64 + 32]; - ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32); - ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8); + let hash_len = header_hash.len(); + buf[..hash_len].copy_from_slice(header_hash); + buf[hash_len..hash_len + mem::size_of::()].copy_from_slice(&nonce.to_ne_bytes()); keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40); - ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32); + buf[64..].copy_from_slice(mix_hash); - // This is initialized in `keccak_256` - let mut hash: [u8; 32] = mem::uninitialized(); + let mut hash = [0u8; 32]; keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len()); hash @@ -205,17 +199,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) let mut buf: MixBuf = MixBuf { half_mix: unsafe { // Pack `header_hash` and `nonce` together - // We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then - // `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array, - // leaving it fully initialized. - let mut out: [u8; NODE_BYTES] = mem::uninitialized(); + let mut out = [0u8; NODE_BYTES]; - ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len()); - ptr::copy_nonoverlapping( - &nonce as *const u64 as *const u8, - out[header_hash.len()..].as_mut_ptr(), - mem::size_of::(), - ); + let hash_len = header_hash.len(); + out[..hash_len].copy_from_slice(header_hash); + out[hash_len..hash_len + mem::size_of::()].copy_from_slice(&nonce.to_ne_bytes()); // compute keccak-512 hash and replicate across mix keccak_512::unchecked( @@ -227,8 +215,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) Node { bytes: out } }, - // This is fully initialized before being read, see `let mut compress = ...` below - compress_bytes: unsafe { mem::uninitialized() }, + compress_bytes: [0u8; MIX_WORDS], }; let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()]; @@ -277,12 +264,16 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) }; { - // This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()` - // times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE- - // ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on + // We iterate precisely `compress.len()` times and set each index, + // leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-ENDIAN MACHINES. + // See a future PR to make this and the rest of the code work correctly on // big-endian arches like mips. let compress: &mut [u32; MIX_WORDS / 4] = unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) }; + #[cfg(target_endian = "big")] + { + compile_error!("parity-ethereum currently only supports little-endian targets"); + } // Compress mix debug_assert_eq!(MIX_WORDS / 4, 8);