mirror of
https://github.com/denoland/deno.git
synced 2025-02-08 07:16:56 -05:00
perf(crypto): use ring for asm implementations of sha256/sha512 (#27885)
Currently we are using the pure rust backend of `sha2`, which has subpar performance compared to asm implementations. We already depend on `ring`, so just use that instead of `sha2` for sha256/sha512 digests. This also speeds up things like S3 uploads, which calculate sha digests of the uploaded objects. On my local machine, this speeds up uploading a 100MB file (to a localhost s3 provider via`@aws-sdk/client-s3`) by about 2x <details> <summary>Benchmark:</summary> ```ts import { createHmac } from "node:crypto"; for ( const size of [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000] ) { const input = "a".repeat(size); Deno.bench({ name: `sha256-${size}`, fn() { const _hash = createHmac("sha256", input).update(input).digest(); }, }); Deno.bench({ name: `sha512-${size}`, fn() { const _hash = createHmac("sha512", input).update(input).digest(); }, }); } ``` </details> <details> <summary>Results (arm64 macOS):</summary> ``` --- sha256-1 --- ../../deno/target/release/deno 2.527 µs 1.240 times faster /Users/nathanwhit/.deno/bin/deno 3.132 µs --- sha512-1 --- ../../deno/target/release/deno 3.364 µs 1.071 times faster /Users/nathanwhit/.deno/bin/deno 3.603 µs --- sha256-10 --- ../../deno/target/release/deno 3.060 µs 1.027 times faster /Users/nathanwhit/.deno/bin/deno 3.144 µs --- sha512-10 --- ../../deno/target/release/deno 3.583 µs 1.047 times faster /Users/nathanwhit/.deno/bin/deno 3.751 µs --- sha256-100 --- ../../deno/target/release/deno 3.695 µs 1.244 times faster /Users/nathanwhit/.deno/bin/deno 4.598 µs --- sha512-100 --- ../../deno/target/release/deno 3.386 µs 1.188 times faster /Users/nathanwhit/.deno/bin/deno 4.021 µs --- sha256-1000 --- ../../deno/target/release/deno 4.007 µs 3.230 times faster /Users/nathanwhit/.deno/bin/deno 12.944 µs --- sha512-1000 --- ../../deno/target/release/deno 6.463 µs 1.466 times faster /Users/nathanwhit/.deno/bin/deno 9.477 µs --- sha256-10000 --- ../../deno/target/release/deno 11.674 µs 6.981 times faster /Users/nathanwhit/.deno/bin/deno 81.493 µs --- sha512-10000 --- ../../deno/target/release/deno 31.250 µs 1.740 times faster /Users/nathanwhit/.deno/bin/deno 54.364 µs --- sha256-100000 --- ../../deno/target/release/deno 82.800 µs 9.393 times faster /Users/nathanwhit/.deno/bin/deno 777.719 µs --- sha512-100000 --- ../../deno/target/release/deno 269.726 µs 1.851 times faster /Users/nathanwhit/.deno/bin/deno 499.243 µs --- sha256-1000000 --- ../../deno/target/release/deno 808.662 µs 9.427 times faster /Users/nathanwhit/.deno/bin/deno 7.623 ms --- sha512-1000000 --- ../../deno/target/release/deno 2.672 ms 1.795 times faster /Users/nathanwhit/.deno/bin/deno 4.795 ms --- sha256-10000000 --- ../../deno/target/release/deno 7.823 ms 9.868 times faster /Users/nathanwhit/.deno/bin/deno 77.201 ms --- sha512-10000000 --- ../../deno/target/release/deno 26.197 ms 1.846 times faster /Users/nathanwhit/.deno/bin/deno 48.356 ms ``` </details> <details> <summary>Results (x86_64 linux):</summary> ``` --- sha256-1 --- /home/nathanwhit/.deno/bin/deno 10.726 µs 1.229 times faster ../../../deno/target/release-lite/deno 13.184 µs --- sha512-1 --- /home/nathanwhit/.deno/bin/deno 13.177 µs 1.051 times faster ../../../deno/target/release-lite/deno 13.845 µs --- sha256-10 --- /home/nathanwhit/.deno/bin/deno 13.156 µs 1.047 times faster ../../../deno/target/release-lite/deno 13.780 µs --- sha512-10 --- /home/nathanwhit/.deno/bin/deno 14.386 µs 1.029 times faster ../../../deno/target/release-lite/deno 14.807 µs --- sha256-100 --- /home/nathanwhit/.deno/bin/deno 14.580 µs 1.083 times faster ../../../deno/target/release-lite/deno 15.789 µs --- sha512-100 --- /home/nathanwhit/.deno/bin/deno 13.477 µs 1.131 times faster ../../../deno/target/release-lite/deno 15.238 µs --- sha256-1000 --- ../../../deno/target/release-lite/deno 17.208 µs 1.116 times faster /home/nathanwhit/.deno/bin/deno 19.198 µs --- sha512-1000 --- ../../../deno/target/release-lite/deno 21.168 µs 1.026 times faster /home/nathanwhit/.deno/bin/deno 21.717 µs --- sha256-10000 --- ../../../deno/target/release-lite/deno 33.586 µs 1.990 times faster /home/nathanwhit/.deno/bin/deno 66.837 µs --- sha512-10000 --- ../../../deno/target/release-lite/deno 53.338 µs 1.009 times faster /home/nathanwhit/.deno/bin/deno 53.817 µs --- sha256-100000 --- ../../../deno/target/release-lite/deno 168.238 µs 3.063 times faster /home/nathanwhit/.deno/bin/deno 515.354 µs --- sha512-100000 --- ../../../deno/target/release-lite/deno 383.311 µs 1.036 times faster /home/nathanwhit/.deno/bin/deno 397.122 µs --- sha256-1000000 --- ../../../deno/target/release-lite/deno 1.474 ms 3.471 times faster /home/nathanwhit/.deno/bin/deno 5.115 ms --- sha512-1000000 --- ../../../deno/target/release-lite/deno 3.658 ms 1.057 times faster /home/nathanwhit/.deno/bin/deno 3.865 ms --- sha256-10000000 --- ../../../deno/target/release-lite/deno 16.438 ms 3.136 times faster /home/nathanwhit/.deno/bin/deno 51.556 ms --- sha512-10000000 --- ../../../deno/target/release-lite/deno 37.128 ms 1.056 times faster /home/nathanwhit/.deno/bin/deno 39.220 ms ``` </details>
This commit is contained in:
parent
1b7719c5d6
commit
bac8171c40
2 changed files with 102 additions and 0 deletions
|
@ -8,6 +8,8 @@ use digest::DynDigest;
|
|||
use digest::ExtendableOutput;
|
||||
use digest::Update;
|
||||
|
||||
mod ring_sha2;
|
||||
|
||||
pub struct Hasher {
|
||||
pub hash: Rc<RefCell<Option<Hash>>>,
|
||||
}
|
||||
|
@ -200,6 +202,24 @@ impl Hash {
|
|||
match algorithm_name {
|
||||
"shake128" => return Ok(Shake128(Default::default(), output_length)),
|
||||
"shake256" => return Ok(Shake256(Default::default(), output_length)),
|
||||
"sha256" => {
|
||||
let digest = ring_sha2::RingSha256::new();
|
||||
if let Some(length) = output_length {
|
||||
if length != digest.output_size() {
|
||||
return Err(HashError::OutputLengthMismatch);
|
||||
}
|
||||
}
|
||||
return Ok(Hash::FixedSize(Box::new(digest)));
|
||||
}
|
||||
"sha512" => {
|
||||
let digest = ring_sha2::RingSha512::new();
|
||||
if let Some(length) = output_length {
|
||||
if length != digest.output_size() {
|
||||
return Err(HashError::OutputLengthMismatch);
|
||||
}
|
||||
}
|
||||
return Ok(Hash::FixedSize(Box::new(digest)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
82
ext/node/ops/crypto/digest/ring_sha2.rs
Normal file
82
ext/node/ops/crypto/digest/ring_sha2.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use digest::generic_array::ArrayLength;
|
||||
|
||||
pub trait RingDigestAlgo {
|
||||
fn algorithm() -> &'static ring::digest::Algorithm;
|
||||
type OutputSize: ArrayLength<u8> + 'static;
|
||||
}
|
||||
|
||||
pub struct RingDigest<Algo: RingDigestAlgo> {
|
||||
context: ring::digest::Context,
|
||||
_phantom: PhantomData<Algo>,
|
||||
}
|
||||
|
||||
impl<Algo: RingDigestAlgo> Clone for RingDigest<Algo> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
context: self.context.clone(),
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Algo: RingDigestAlgo> digest::HashMarker for RingDigest<Algo> {}
|
||||
impl<Algo: RingDigestAlgo> Default for RingDigest<Algo> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
context: ring::digest::Context::new(Algo::algorithm()),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Algo: RingDigestAlgo> digest::Reset for RingDigest<Algo> {
|
||||
fn reset(&mut self) {
|
||||
self.context = ring::digest::Context::new(Algo::algorithm())
|
||||
}
|
||||
}
|
||||
impl<Algo: RingDigestAlgo> digest::Update for RingDigest<Algo> {
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
self.context.update(data);
|
||||
}
|
||||
}
|
||||
impl<Algo: RingDigestAlgo> digest::OutputSizeUser for RingDigest<Algo> {
|
||||
type OutputSize = Algo::OutputSize;
|
||||
}
|
||||
impl<Algo: RingDigestAlgo> digest::FixedOutput for RingDigest<Algo> {
|
||||
fn finalize_into(self, out: &mut digest::Output<Self>) {
|
||||
let result = self.context.finish();
|
||||
out.copy_from_slice(result.as_ref());
|
||||
}
|
||||
}
|
||||
impl<Algo: RingDigestAlgo> digest::FixedOutputReset for RingDigest<Algo> {
|
||||
fn finalize_into_reset(&mut self, out: &mut digest::Output<Self>) {
|
||||
let context = std::mem::replace(
|
||||
&mut self.context,
|
||||
ring::digest::Context::new(Algo::algorithm()),
|
||||
);
|
||||
out.copy_from_slice(context.finish().as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RingSha256Algo;
|
||||
impl RingDigestAlgo for RingSha256Algo {
|
||||
fn algorithm() -> &'static ring::digest::Algorithm {
|
||||
&ring::digest::SHA256
|
||||
}
|
||||
|
||||
type OutputSize = digest::typenum::U32;
|
||||
}
|
||||
pub struct RingSha512Algo;
|
||||
impl RingDigestAlgo for RingSha512Algo {
|
||||
fn algorithm() -> &'static ring::digest::Algorithm {
|
||||
&ring::digest::SHA512
|
||||
}
|
||||
|
||||
type OutputSize = digest::typenum::U64;
|
||||
}
|
||||
|
||||
pub type RingSha256 = RingDigest<RingSha256Algo>;
|
||||
pub type RingSha512 = RingDigest<RingSha512Algo>;
|
Loading…
Add table
Reference in a new issue