[ethash] remove mem::uninitialized (#10861)
* [ethash] replace mem::uninitialized with MaybeUninit * [ethash] replace another occurence of mem::uninitialized * [ethash] remove last mem::uninitialized * [ethash] clean up benches * [ethash] update outdated comment * [ethash] compile error on big endian targets * [ethash] extract 32 into a constant * [ethash] rename the constant to KECCAK_LEN * [ethash] bench quick_get_difficulty * [ethash] remove MaybeUninit completely * [ethash] replace ptr::copy_nonoverlapping with copy_from_slice * [ethash] s/header_len/hash_len * [ethash] remove duplication in bench * [ethash] add a config for basic benches * [ethash] fix a typo in bench fn name * [ethash] remove needless cast
This commit is contained in:
parent
d850eb0dd5
commit
5a131175e8
@ -21,22 +21,39 @@ extern crate ethash;
|
|||||||
use criterion::Criterion;
|
use criterion::Criterion;
|
||||||
use ethash::{NodeCacheBuilder, OptimizeFor};
|
use ethash::{NodeCacheBuilder, OptimizeFor};
|
||||||
|
|
||||||
const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe,
|
const HASH: [u8; 32] = [
|
||||||
0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f,
|
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b,
|
||||||
0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
|
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;
|
const NONCE: u64 = 0xd7b3ac70a301a249;
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group! {
|
||||||
basic,
|
name = basic;
|
||||||
bench_light_compute_memmap,
|
config = dont_take_an_eternity_to_run();
|
||||||
|
targets = bench_light_compute_memmap,
|
||||||
bench_light_compute_memory,
|
bench_light_compute_memory,
|
||||||
bench_light_new_round_trip_memmap,
|
bench_light_new_round_trip_memmap,
|
||||||
bench_light_new_round_trip_memory,
|
bench_light_new_round_trip_memory,
|
||||||
bench_light_from_file_round_trip_memory,
|
bench_light_from_file_round_trip_memory,
|
||||||
bench_light_from_file_round_trip_memmap
|
bench_light_from_file_round_trip_memmap,
|
||||||
);
|
bench_quick_get_difficulty,
|
||||||
|
}
|
||||||
criterion_main!(basic);
|
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) {
|
fn bench_light_compute_memmap(b: &mut Criterion) {
|
||||||
use std::env;
|
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 builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||||
let light = builder.light(&env::temp_dir(), 486382);
|
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) {
|
fn bench_light_new_round_trip_memmap(b: &mut Criterion) {
|
||||||
use std::env;
|
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 builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||||
let light = builder.light(&env::temp_dir(), 486382);
|
let light = builder.light(&env::temp_dir(), 486382);
|
||||||
light.compute(&HASH, NONCE, u64::max_value());
|
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) {
|
fn bench_light_new_round_trip_memory(b: &mut Criterion) {
|
||||||
use std::env;
|
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 builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||||
let light = builder.light(&env::temp_dir(), 486382);
|
let light = builder.light(&env::temp_dir(), 486382);
|
||||||
light.compute(&HASH, NONCE, u64::max_value());
|
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();
|
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 builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||||
light.compute(&HASH, NONCE, u64::max_value());
|
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();
|
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 builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||||
light.compute(&HASH, NONCE, u64::max_value());
|
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[..]);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
@ -26,7 +26,7 @@ use seed_compute::SeedHashCompute;
|
|||||||
use shared::*;
|
use shared::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use std::{mem, ptr};
|
use std::mem;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
|
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]);
|
let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]);
|
||||||
keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash))
|
keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash))
|
||||||
} else {
|
} else {
|
||||||
// This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set
|
let mut buf = [0u8; 64 + 32];
|
||||||
// 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();
|
|
||||||
|
|
||||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32);
|
let hash_len = header_hash.len();
|
||||||
ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8);
|
buf[..hash_len].copy_from_slice(header_hash);
|
||||||
|
buf[hash_len..hash_len + mem::size_of::<u64>()].copy_from_slice(&nonce.to_ne_bytes());
|
||||||
|
|
||||||
keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40);
|
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 = [0u8; 32];
|
||||||
let mut hash: [u8; 32] = mem::uninitialized();
|
|
||||||
keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
||||||
|
|
||||||
hash
|
hash
|
||||||
@ -205,17 +199,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
let mut buf: MixBuf = MixBuf {
|
let mut buf: MixBuf = MixBuf {
|
||||||
half_mix: unsafe {
|
half_mix: unsafe {
|
||||||
// Pack `header_hash` and `nonce` together
|
// Pack `header_hash` and `nonce` together
|
||||||
// We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then
|
let mut out = [0u8; NODE_BYTES];
|
||||||
// `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();
|
|
||||||
|
|
||||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len());
|
let hash_len = header_hash.len();
|
||||||
ptr::copy_nonoverlapping(
|
out[..hash_len].copy_from_slice(header_hash);
|
||||||
&nonce as *const u64 as *const u8,
|
out[hash_len..hash_len + mem::size_of::<u64>()].copy_from_slice(&nonce.to_ne_bytes());
|
||||||
out[header_hash.len()..].as_mut_ptr(),
|
|
||||||
mem::size_of::<u64>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// compute keccak-512 hash and replicate across mix
|
// compute keccak-512 hash and replicate across mix
|
||||||
keccak_512::unchecked(
|
keccak_512::unchecked(
|
||||||
@ -227,8 +215,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
|
|
||||||
Node { bytes: out }
|
Node { bytes: out }
|
||||||
},
|
},
|
||||||
// This is fully initialized before being read, see `let mut compress = ...` below
|
compress_bytes: [0u8; MIX_WORDS],
|
||||||
compress_bytes: unsafe { mem::uninitialized() },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()];
|
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) };
|
let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) };
|
||||||
|
|
||||||
{
|
{
|
||||||
// This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()`
|
// We iterate precisely `compress.len()` times and set each index,
|
||||||
// times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-
|
// leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-ENDIAN MACHINES.
|
||||||
// ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on
|
// See a future PR to make this and the rest of the code work correctly on
|
||||||
// big-endian arches like mips.
|
// big-endian arches like mips.
|
||||||
let compress: &mut [u32; MIX_WORDS / 4] =
|
let compress: &mut [u32; MIX_WORDS / 4] =
|
||||||
unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) };
|
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
|
// Compress mix
|
||||||
debug_assert_eq!(MIX_WORDS / 4, 8);
|
debug_assert_eq!(MIX_WORDS / 4, 8);
|
||||||
|
Loading…
Reference in New Issue
Block a user