Use hardware acceleration for SHA256 (#2426)

## Proposed Changes

Modify the SHA256 implementation in `eth2_hashing` so that it switches between `ring` and `sha2` to take advantage of [x86_64 SHA extensions](https://en.wikipedia.org/wiki/Intel_SHA_extensions). The extensions are available on modern Intel and AMD CPUs, and seem to provide a considerable speed-up: on my Ryzen 5950X it dropped state tree hashing times by about 30% from 35ms to 25ms (on Prater).

## Additional Info

The extensions became available in the `sha2` crate [last year](https://www.reddit.com/r/rust/comments/hf2vcx/ann_rustcryptos_sha1_and_sha2_now_support/), and are not available in Ring, which uses a [pure Rust implementation of sha2](https://github.com/briansmith/ring/blob/main/src/digest/sha2.rs). Ring is faster on CPUs that lack the extensions so I've implemented a runtime switch to use `sha2` only when the extensions are available. The runtime switching seems to impose a miniscule penalty (see the benchmarks linked below).
This commit is contained in:
Michael Sproul
2021-07-12 08:47:01 +00:00
parent a7b7134abb
commit 2c691af95b
10 changed files with 222 additions and 86 deletions

View File

@@ -1,5 +1,5 @@
use crate::Hash256;
use eth2_hashing::{Context, SHA256};
use eth2_hashing::{Context, Sha256Context};
use std::cmp::max;
/// Return `p(index)` in a pseudorandom permutation `p` of `0...list_size-1` with ``seed`` as entropy.
@@ -54,7 +54,7 @@ fn do_round(seed: &[u8], index: usize, pivot: usize, round: u8, list_size: usize
}
fn hash_with_round_and_position(seed: &[u8], round: u8, position: usize) -> Hash256 {
let mut context = Context::new(&SHA256);
let mut context = Context::new();
context.update(seed);
context.update(&[round]);
@@ -64,17 +64,17 @@ fn hash_with_round_and_position(seed: &[u8], round: u8, position: usize) -> Hash
*/
context.update(&(position / 256).to_le_bytes()[0..4]);
let digest = context.finish();
let digest = context.finalize();
Hash256::from_slice(digest.as_ref())
}
fn hash_with_round(seed: &[u8], round: u8) -> Hash256 {
let mut context = Context::new(&SHA256);
let mut context = Context::new();
context.update(seed);
context.update(&[round]);
let digest = context.finish();
let digest = context.finalize();
Hash256::from_slice(digest.as_ref())
}

View File

@@ -1,5 +1,5 @@
use crate::Hash256;
use eth2_hashing::{Context, SHA256};
use eth2_hashing::hash_fixed;
use std::mem;
const SEED_SIZE: usize = 32;
@@ -31,12 +31,10 @@ impl Buf {
/// Returns the new pivot. It is "raw" because it has not modulo the list size (this must be
/// done by the caller).
fn raw_pivot(&self) -> u64 {
let mut context = Context::new(&SHA256);
context.update(&self.0[0..PIVOT_VIEW_SIZE]);
let digest = context.finish();
let digest = hash_fixed(&self.0[0..PIVOT_VIEW_SIZE]);
let mut bytes = [0; mem::size_of::<u64>()];
bytes[..].copy_from_slice(&digest.as_ref()[0..mem::size_of::<u64>()]);
bytes[..].copy_from_slice(&digest[0..mem::size_of::<u64>()]);
u64::from_le_bytes(bytes)
}
@@ -47,10 +45,7 @@ impl Buf {
/// Hash the entire buffer.
fn hash(&self) -> Hash256 {
let mut context = Context::new(&SHA256);
context.update(&self.0[..]);
let digest = context.finish();
Hash256::from_slice(digest.as_ref())
Hash256::from_slice(&hash_fixed(&self.0))
}
}