static-generics 0.1.0

Truly zero-cost generic statics for Rust (1-2 instructions per access). Based on cynecx/generic-statics.
Documentation

static-generics

docs.rs License: MIT OR Apache-2.0

Zero-cost generic statics for Rust.

Based on the ideas of cynecx/generic-statics, and implemented to be zero-cost*: one–two instructions to access a generic (lea, adrp+add, auipc+addi, larl, …) instead of a call to the accessor function.

*Zero cost applies only to supported platforms, unsupported platforms go through fallback of HashMap + Mutex and require std feature to be enabled.

use static_generics::{define_namespace, namespace::NamespaceExt};
use core::sync::atomic::{AtomicU64, Ordering};

define_namespace!(Counters);

let hits = Counters::get::<AtomicU64>();
hits.fetch_add(1, Ordering::Relaxed);

Why

Rust has no generic statics unlike C++, and default solution to that is using typemap/HashMap<TypeId, ...> which require locking, allocation, hashing on each access which is expensive and slow. static-generics implements zero-cost solution which allows to implement generic statics in ~1-2 instructions on supported platforms (and falls back to hashmap on unsupported).

Requirements and limits

  • Only bytemuck::Zeroable types can be stored in the static generic (use OnceSlot to lazily initialize any other types in static generic).
  • No Drop support, all static generics are essentially leaked. If you need to clean-up memory after them, it has to be done manually.
  • Dynamic library loading is not guaranteed to return the same address for generic_static::<i32>() done in libA.so and libB.so.

Implementation

We use inline assembly + monomorphization of rustc to emit "unique" function body per each T. In the assembly code block we reserve the storage .comm section with the name of unique function + gen_static_ prefix, and after that we compute the PC-relative address using lea on x86, adrp on arm, and so on.

Since .comm section stays the same across binary per each T we can safely inline generic_static calls and guarantee 1 or 2 instruction access to said generics.

Platform support

  • x86_64
  • aarch64
  • x86_32
  • arm (Thumb as well)
  • riscv32/riscv64
  • loongarc32/loongarch64
  • powerpc/powerpc64
  • s390x

On other platforms (or with Miri/Cranelift detected) the crate will fallback to slow implementation via HashMap and Mutex.