Merge pull request #6080 from Vurich/ethash
Unroll loops in light_compute
This commit is contained in:
commit
079b24175c
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -316,6 +316,11 @@ name = "crossbeam"
|
|||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypt32-sys"
|
name = "crypt32-sys"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -445,6 +450,7 @@ dependencies = [
|
|||||||
name = "ethash"
|
name = "ethash"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crunchy 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -3180,6 +3186,7 @@ dependencies = [
|
|||||||
"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
|
"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
|
||||||
"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
|
"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
|
||||||
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
|
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
|
||||||
|
"checksum crunchy 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6aa9cb5f2d7bffc4eecfaf924fe450549dc4f0c3a6502298dc24f968b1eabbe"
|
||||||
"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
|
"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
|
||||||
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
|
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
|
||||||
"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
||||||
|
@ -10,3 +10,7 @@ log = "0.3"
|
|||||||
sha3 = { path = "../util/sha3" }
|
sha3 = { path = "../util/sha3" }
|
||||||
primal = "0.2.3"
|
primal = "0.2.3"
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
|
crunchy = "0.1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
benches = []
|
@ -289,24 +289,55 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
|||||||
let num_full_pages = (full_size / page_size) as u32;
|
let num_full_pages = (full_size / page_size) as u32;
|
||||||
let cache: &[Node] = &light.cache; // deref once for better performance
|
let cache: &[Node] = &light.cache; // deref once for better performance
|
||||||
|
|
||||||
for i in 0..(ETHASH_ACCESSES as u32) {
|
debug_assert_eq!(ETHASH_ACCESSES, 64);
|
||||||
let index = fnv_hash(f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i, *mix.get_unchecked(0).as_words().get_unchecked((i as usize) % MIX_WORDS)) % num_full_pages;
|
debug_assert_eq!(MIX_NODES, 2);
|
||||||
for n in 0..MIX_NODES {
|
debug_assert_eq!(NODE_WORDS, 16);
|
||||||
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, cache);
|
|
||||||
for w in 0..NODE_WORDS {
|
unroll! {
|
||||||
*mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) = fnv_hash(*mix.get_unchecked(n).as_words().get_unchecked(w), *tmp_node.as_words().get_unchecked(w));
|
// ETHASH_ACCESSES
|
||||||
|
for i_usize in 0..64 {
|
||||||
|
let i = i_usize as u32;
|
||||||
|
|
||||||
|
let index = fnv_hash(
|
||||||
|
f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i,
|
||||||
|
*mix.get_unchecked(0).as_words().get_unchecked(i as usize % MIX_WORDS)
|
||||||
|
) % num_full_pages;
|
||||||
|
|
||||||
|
unroll! {
|
||||||
|
// MIX_NODES
|
||||||
|
for n in 0..2 {
|
||||||
|
let tmp_node = calculate_dag_item(
|
||||||
|
index * MIX_NODES as u32 + n as u32,
|
||||||
|
cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
unroll! {
|
||||||
|
// NODE_WORDS
|
||||||
|
for w in 0..16 {
|
||||||
|
*mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) =
|
||||||
|
fnv_hash(
|
||||||
|
*mix.get_unchecked(n).as_words().get_unchecked(w),
|
||||||
|
*tmp_node.as_words().get_unchecked(w),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(MIX_WORDS / 4, 8);
|
||||||
|
|
||||||
// compress mix
|
// compress mix
|
||||||
for i in 0..(MIX_WORDS / 4) {
|
unroll! {
|
||||||
let w = i * 4;
|
for i in 0..8 {
|
||||||
let mut reduction = *mix.get_unchecked(0).as_words().get_unchecked(w + 0);
|
let w = i * 4;
|
||||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 1);
|
let mut reduction = *mix.get_unchecked(0).as_words().get_unchecked(w + 0);
|
||||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 2);
|
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 1);
|
||||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 3);
|
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 2);
|
||||||
*mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(i) = reduction;
|
reduction = reduction.wrapping_mul(FNV_PRIME) ^ *mix.get_unchecked(0).as_words().get_unchecked(w + 3);
|
||||||
|
*mix.get_unchecked_mut(0).as_words_mut().get_unchecked_mut(i) = reduction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mix_hash = [0u8; 32];
|
let mut mix_hash = [0u8; 32];
|
||||||
@ -331,13 +362,22 @@ fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
|||||||
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
|
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
|
||||||
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
|
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
|
||||||
|
|
||||||
for i in 0..ETHASH_DATASET_PARENTS {
|
debug_assert_eq!(NODE_WORDS, 16);
|
||||||
|
for i in 0..ETHASH_DATASET_PARENTS as u32 {
|
||||||
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
|
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
|
||||||
let parent = cache.get_unchecked(parent_index as usize);
|
let parent = cache.get_unchecked(parent_index as usize);
|
||||||
for w in 0..NODE_WORDS {
|
|
||||||
*ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w));
|
unroll! {
|
||||||
|
for w in 0..16 {
|
||||||
|
*ret.as_words_mut().get_unchecked_mut(w) =
|
||||||
|
fnv_hash(
|
||||||
|
*ret.as_words().get_unchecked(w),
|
||||||
|
*parent.as_words().get_unchecked(w)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
|
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@ -359,13 +399,20 @@ fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
|
|||||||
sha3::sha3_512(nodes.get_unchecked_mut(i).bytes.as_mut_ptr(), NODE_BYTES, nodes.get_unchecked(i - 1).bytes.as_ptr(), NODE_BYTES);
|
sha3::sha3_512(nodes.get_unchecked_mut(i).bytes.as_mut_ptr(), NODE_BYTES, nodes.get_unchecked(i - 1).bytes.as_ptr(), NODE_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(NODE_WORDS, 16);
|
||||||
|
|
||||||
|
// This _should_ get unrolled by the compiler, since it's not using the loop variable.
|
||||||
for _ in 0..ETHASH_CACHE_ROUNDS {
|
for _ in 0..ETHASH_CACHE_ROUNDS {
|
||||||
for i in 0..num_nodes {
|
for i in 0..num_nodes {
|
||||||
let idx = *nodes.get_unchecked_mut(i).as_words().get_unchecked(0) as usize % num_nodes;
|
let idx = *nodes.get_unchecked_mut(i).as_words().get_unchecked(0) as usize % num_nodes;
|
||||||
let mut data = nodes.get_unchecked((num_nodes - 1 + i) % num_nodes).clone();
|
let mut data = nodes.get_unchecked((num_nodes - 1 + i) % num_nodes).clone();
|
||||||
for w in 0..NODE_WORDS {
|
|
||||||
*data.as_words_mut().get_unchecked_mut(w) ^= *nodes.get_unchecked(idx).as_words().get_unchecked(w);
|
unroll! {
|
||||||
|
for w in 0..16 {
|
||||||
|
*data.as_words_mut().get_unchecked_mut(w) ^= *nodes.get_unchecked(idx).as_words().get_unchecked(w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sha3_512(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
sha3_512(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
//! Ethash implementation
|
//! Ethash implementation
|
||||||
//! See https://github.com/ethereum/wiki/wiki/Ethash
|
//! See https://github.com/ethereum/wiki/wiki/Ethash
|
||||||
|
|
||||||
|
#![cfg_attr(feature = "benches", feature(test))]
|
||||||
|
|
||||||
extern crate primal;
|
extern crate primal;
|
||||||
extern crate sha3;
|
extern crate sha3;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate crunchy;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
mod compute;
|
mod compute;
|
||||||
@ -128,3 +133,27 @@ fn test_lru() {
|
|||||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
|
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
|
||||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "benches")]
|
||||||
|
mod benchmarks {
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use compute::{Light, light_compute, SeedHashCompute};
|
||||||
|
use self::test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_light_compute(b: &mut Bencher) {
|
||||||
|
let hash = [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];
|
||||||
|
let nonce = 0xd7b3ac70a301a249;
|
||||||
|
let light = Light::new(486382);
|
||||||
|
|
||||||
|
b.iter(|| light_compute(&light, &hash, nonce));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_seedhash(b: &mut Bencher) {
|
||||||
|
let seed_compute = SeedHashCompute::new();
|
||||||
|
|
||||||
|
b.iter(|| seed_compute.get_seedhash(486382));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user