generic-static-cache 0.2.1

Generic static storage in generic functions
Documentation
use criterion::{Criterion, criterion_group, criterion_main};
use generic_static_cache::*;
use std::{
    any::TypeId,
    collections::HashMap,
    hash::{BuildHasher, Hasher},
    hint::black_box,
    marker::PhantomData,
    sync::{
        RwLock,
        atomic::{AtomicI32, Ordering},
    },
};

#[allow(clippy::extra_unused_type_parameters)]
#[inline]
fn with_macro<T: 'static>() -> i32 {
    generic_static!(
        static COUNT: &AtomicI32 = &AtomicI32::new(1);
    );
    COUNT.load(Ordering::Relaxed)
}

#[inline]
fn with_global<T: Sync + 'static>() -> i32 {
    let global = &global::<(PhantomData<T>, AtomicI32)>().1;
    global.load(Ordering::Relaxed)
}

#[inline]
fn with_map<T: Sync + 'static>() -> i32 {
    pub(crate) struct SyncWrapper(AtomicI32);
    unsafe impl Sync for SyncWrapper {}
    unsafe impl Send for SyncWrapper {}
    static MAP: RwLock<TypeIdMap<SyncWrapper>> =
        RwLock::new(TypeIdMap::with_hasher(NoOpTypeIdBuildHasher));

    {
        let guard = MAP.read().unwrap();
        if let Some(global) = guard.get(&TypeId::of::<T>()) {
            return global.0.load(Ordering::Relaxed);
        }
    }
    let mut guard = MAP.write().unwrap();
    guard.insert(TypeId::of::<T>(), SyncWrapper(AtomicI32::new(0)));
    0
}

type TypeIdMap<T> = HashMap<TypeId, T, NoOpTypeIdBuildHasher>;

#[doc(hidden)]
#[derive(Default)]
struct NoOpTypeIdBuildHasher;

impl BuildHasher for NoOpTypeIdBuildHasher {
    type Hasher = NoOpTypeIdHasher;

    fn build_hasher(&self) -> Self::Hasher {
        NoOpTypeIdHasher(0)
    }
}

#[doc(hidden)]
#[derive(Default)]
struct NoOpTypeIdHasher(u64);

impl Hasher for NoOpTypeIdHasher {
    fn finish(&self) -> u64 {
        self.0
    }

    fn write(&mut self, _bytes: &[u8]) {
        unimplemented!()
    }

    fn write_u64(&mut self, i: u64) {
        self.0 = i
    }
}

fn criterion_benchmark(c: &mut Criterion) {
    let inner = 10_000;
    c.bench_function("macro", |b| {
        b.iter(|| {
            let mut acc = 0;
            for _ in 0..inner {
                acc += black_box(with_macro::<u8>());
                acc += black_box(with_macro::<u16>());
                acc += black_box(with_macro::<u32>());
                acc += black_box(with_macro::<u64>());
                acc += black_box(with_macro::<i8>());
                acc += black_box(with_macro::<i16>());
                acc += black_box(with_macro::<i32>());
                acc += black_box(with_macro::<i64>());
            }
            black_box(acc);
        })
    });
    c.bench_function("global", |b| {
        b.iter(|| {
            let mut acc = 0;
            for _ in 0..inner {
                acc += black_box(with_global::<u8>());
                acc += black_box(with_global::<u16>());
                acc += black_box(with_global::<u32>());
                acc += black_box(with_global::<u64>());
                acc += black_box(with_global::<i8>());
                acc += black_box(with_global::<i16>());
                acc += black_box(with_global::<i32>());
                acc += black_box(with_global::<i64>());
            }
            black_box(acc);
        })
    });
    c.bench_function("map", |b| {
        b.iter(|| {
            let mut acc = 0;
            for _ in 0..inner {
                acc += black_box(with_map::<u8>());
                acc += black_box(with_map::<u16>());
                acc += black_box(with_map::<u32>());
                acc += black_box(with_map::<u64>());
                acc += black_box(with_map::<i8>());
                acc += black_box(with_map::<i16>());
                acc += black_box(with_map::<i32>());
                acc += black_box(with_map::<i64>());
            }
            black_box(acc);
        })
    });
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);