This is an automated archive made by the Lemmit Bot.

The original was posted on /r/rust by /u/danielcota on 2025-05-27 04:10:50+00:00.


I’ve been working on biski64, a pseudo-random number generator with the goals of high speed, a guaranteed period, and empirical robustness for non-cryptographic tasks. I’ve just finished the Rust implementation and would love to get your feedback on the design and performance.

Key Highlights:

  • Extremely Fast: Benchmarked at ~0.37 ns per u64 on my machine (Ryzen 9 7950X3D). This was 150% faster than the rand_xoshiro crate (0.94 ns) and 14% faster than the rand-wyrand crate (0.43 ns) in the same test.
  • no_std Compatible: The core generator has zero dependencies and is fully no_std, making it suitable for embedded and other resource-constrained environments.
  • Statistically Robust: Passes PractRand up to 32TB. The README also details results from running TestU01’s BigCrush 100 times and comparing it against other established PRNGs.
  • Guaranteed Period: Incorporates a 64-bit Weyl sequence to ensure a minimum period of 264.
  • Parallel Streams: The design allows for trivially creating independent parallel streams.
  • rand Crate Integration: The library provides an implementation of the rand crate’s RngCore and SeedableRng traits, so it can be used as a drop-in replacement anywhere the rand API is used.

Installation:

Add biski64 to your Cargo.toml dependencies:

[dependencies]
biski64 = "0.1.6"

Basic Usage

use rand::{RngCore, SeedableRng};
use biski64::Biski64Rng;

let mut rng = Biski64Rng::seed_from_u64(12345);
let num = rng.next_u64();

Algorithm: Here is the core next_u64 function. The state is just five u64 values.

// core logic uses Wrapping<u64> for well-defined overflow
const GR: Wrapping<u64> = Wrapping(0x9e3779b97f4a7c15);

#[inline(always)]
pub fn next_u64(&mut self) -> u64 {
    let old_output = self.output;
    let new_mix = self.old_rot + self.output;

    self.output = GR * self.mix;
    self.old_rot = Wrapping(self.last_mix.0.rotate_left(18));

    self.last_mix = self.fast_loop ^ self.mix;
    self.mix = new_mix;

    self.fast_loop += GR;

    old_output.0
}

(The repo includes the full, documented code and benchmarks.)

I’m particularly interested in your thoughts on the API design and any potential improvements for making it more ergonomic for Rust developers.

Thanks for taking a look!