From 79d25552db013b6452b1fc3491f9d35057efd4f6 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Mon, 5 Aug 2024 03:07:53 -0700 Subject: [PATCH 1/2] Avoid resolving the `Adler32Imp` to use multiple times. --- src/imp/mod.rs | 38 ++++++++++++++++++++++++++++++++++++-- src/lib.rs | 13 ++----------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/imp/mod.rs b/src/imp/mod.rs index 957b50a..2cd4c4d 100644 --- a/src/imp/mod.rs +++ b/src/imp/mod.rs @@ -1,3 +1,5 @@ +use core::sync::atomic::{AtomicPtr, Ordering}; + pub mod avx2; pub mod avx512; pub mod scalar; @@ -5,7 +7,7 @@ pub mod sse2; pub mod ssse3; pub mod wasm; -pub type Adler32Imp = fn(u16, u16, &[u8]) -> (u16, u16); +type Adler32Imp = fn(u16, u16, &[u8]) -> (u16, u16); #[inline] #[allow(non_snake_case)] @@ -13,7 +15,7 @@ pub const fn _MM_SHUFFLE(z: u32, y: u32, x: u32, w: u32) -> i32 { ((z << 6) | (y << 4) | (x << 2) | w) as i32 } -pub fn get_imp() -> Adler32Imp { +fn get_imp() -> Adler32Imp { avx512::get_imp() .or_else(avx2::get_imp) .or_else(ssse3::get_imp) @@ -21,3 +23,35 @@ pub fn get_imp() -> Adler32Imp { .or_else(wasm::get_imp) .unwrap_or(scalar::update) } + +#[inline] +const fn adler_imp_to_raw_pointer(imp: Adler32Imp) -> *mut () { + // Safety: Equivalent to `imp as usize as *mut ()`, but avoids pointer-to-int + // casts which are lossy in terms of provenance. + unsafe { core::mem::transmute(imp) } +} + +// This either contains the resolver function (initially), or the +// already-resolved `Adler32Imp` (after the first call). +static IMP: AtomicPtr<()> = AtomicPtr::new(adler_imp_to_raw_pointer(resolve_and_call)); +// Initial value of `IMP`. This resolves the implementation to use, stores it in +// IMP (so that all calls after the first skip resolving), and then forwards the +// arguments it gets to the implementation it resolved. +fn resolve_and_call(a: u16, b: u16, data: &[u8]) -> (u16, u16) { + let resolved_imp = get_imp(); + let imp_as_raw_ptr = adler_imp_to_raw_pointer(resolved_imp); + // Ensure the next call goes directly to the resolved implementation. + IMP.store(imp_as_raw_ptr, Ordering::Relaxed); + // Forward the arguments on. + resolved_imp(a, b, data) +} + +/// Loads and invokes the implementation, resolving it if needed (only needed +/// the first time through). +#[inline] +pub fn call(a: u16, b: u16, data: &[u8]) -> (u16, u16) { + let imp = IMP.load(Ordering::Relaxed); + // Safety: `IMP` only ever contains valid `Adler32Imp`s. + let imp: Adler32Imp = unsafe { core::mem::transmute(imp) }; + imp(a, b, data) +} diff --git a/src/lib.rs b/src/lib.rs index 1ee545e..af45681 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,15 +87,11 @@ pub mod hash; #[doc(hidden)] pub mod imp; -pub use hash::*; -use imp::{get_imp, Adler32Imp}; - /// An adler32 hash generator type. #[derive(Clone)] pub struct Adler32 { a: u16, b: u16, - update: Adler32Imp, } impl Adler32 { @@ -129,13 +125,12 @@ impl Adler32 { Self { a: checksum as u16, b: (checksum >> 16) as u16, - update: get_imp(), } } /// Computes hash for supplied data and stores results in internal state. pub fn write(&mut self, data: &[u8]) { - let (a, b) = (self.update)(self.a, self.b, data); + let (a, b) = imp::call(self.a, self.b, data); self.a = a; self.b = b; @@ -181,11 +176,7 @@ pub trait Adler32Hash { impl Default for Adler32 { fn default() -> Self { - Self { - a: 1, - b: 0, - update: get_imp(), - } + Self { a: 1, b: 0 } } } From ee1c1e1d8d5d9e2d37d4a21152ceeb98f0298638 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Mon, 5 Aug 2024 03:16:42 -0700 Subject: [PATCH 2/2] Fix stale comment --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index af45681..7519156 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,9 +112,6 @@ impl Adler32 { /// Constructs a new `Adler32` using existing checksum. /// - /// Potential overhead here due to runtime feature detection although in testing on 100k - /// and 10k random byte arrays it was not really noticeable. - /// /// # Examples /// ```rust /// use simd_adler32::Adler32;