pgregory.net/rand

repository·master·Indexed 42 days ago

https://github.com/flyingmutant/rand

A high-performance, high-quality random number generation library for Go designed as a drop-in replacement for math/rand. It features API compatibility with *rand.Rand methods, simplified initialization via rand.New(), and utilizes the SFC64 algorithm for speed and quality. The library provides deterministic generation across architectures and includes specialized functions like ShuffleSlice and ExpFloat64. It is optimized for high-concurrency environments by leveraging goroutine-local state to avoid global mutex bottlenecks.

Tokens
5.3K
Snippets
24
Records
30
Agent score
53%

What's inside rand

  1. Overview of pgregory.net/rand

    master

    pgregory.net/rand

    pgregory.net/rand is a fast, high-quality alternative to Go's standard math/rand and golang.org/x/exp/rand packages.

    Key Features:

    • API Compatibility: It is compatible with all *rand.Rand methods and most top-level functions (except Seed()).
    • Performance: Significantly faster than standard library alternatives.
    • Improved Quality: Provides higher quality random number generation.
    • Simplified Initialization: Uses a more direct approach to creating new generators compared to the standard library.
  2. Understanding the design choices of pgregory.net/rand

    master

    The package is designed for high-concurrency and high-performance environments with specific architectural decisions:

    • No Source abstraction: In Go, the Source abstraction is considered a costly overhead that provides little value for most users.
    • No top-level Seed(): To avoid the performance bottleneck of a global mutex-protected state, the package avoids a top-level Seed() function. Instead, it leverages goroutine-local state to ensure scalability as parallelism increases.
    • Use of sfc64: The package uses sfc64 because it is fast, has withstood extensive testing without known flaws, and provides strong guarantees regarding period length and seed distance.
  3. Stability and Determinism in pgregory.net/rand

    master

    The pgregory.net/rand package is considered stable. It guarantees:

    • API Stability: The public interface is stable.
    • Deterministic Generation: Deterministic pseudo-random generation produces identical results across 32-bit and 64-bit architectures, and across both little-endian and big-endian systems.

    Any changes to these deterministic results will only occur during a major version bump.

  4. How pgregory.net/rand works

    master

    The package achieves high performance and quality by utilizing four core primitives:

    1. Raw 64-bit generation: Uses sfc64 or goroutine-local runtime.fastrand64().
    2. Floating-point generation: Implemented via floating-point multiplication.
    3. Integer generation in range: Uses 32.64 or 64.128 fixed-point multiplication.

    On most modern platforms, top-level functions use goroutine-local wyrand generators provided by the Go runtime to balance quality and concurrent execution speed.

  5. Initialize a new random generator

    master

    To create a new random generator instance, use rand.New(). Unlike the standard library, which often requires manual seeding via rand.NewSource(time.Now().UnixNano()), pgregory.net/rand provides simplified initialization patterns:

    • Use rand.New() for a default generator.
    • Use rand.New(seed) (where seed is a uint64) to initialize with a specific seed.
    // Default initialization
    r := rand.New()
    
    // Seeded initialization
    r := rand.New(1)
  6. Choosing between pgregory.net/rand and math/rand/v2

    master

    Deciding which random number generator to use depends on your performance requirements:

    • Use math/rand/v2 if you are not performing nanosecond-level performance profiling. It is a significant improvement over the original math/rand and is suitable for most general-purpose applications.
    • Use pgregory.net/rand if you are counting nanoseconds and require maximum speed without compromising the quality of the random numbers.
  7. Use the Rand type for pseudo-random number generation

    master

    The Rand type is a pseudo-random number generator based on the SFC64 algorithm. It is designed for high speed and high quality, making it faster and better than the standard math/rand package, but it is unsuitable for security-sensitive work (use crypto/rand instead).

    Key Characteristics:

    • State: Has 256 bits of state with an average period of ~2^255.
    • Concurrency: Methods on a *Rand instance are not safe for concurrent use. If you need concurrent access, use the top-level package functions (like rand.Int()) which use goroutine-local sources.
    • Determinism: Using New(seed...) or the Seed(uint64) method allows for reproducible sequences.
    r := rand.New(42)
    val := r.Uint64()
  8. Compare pgregory.net/rand performance with math/rand/v2

    master

    The following benchmarks compare pgregory.net/rand against math/rand/v2 (using top-level functions and PCG source) on an Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz, linux/amd64 environment. While some raw integer operations are comparable, many generator methods (like Float64, Intn, and Uint64) show significant speed improvements in pgregory.net/rand.

    name                    old time/op    new time/op    delta
    Float64-8                 3.29ns ± 2%    3.55ns ±15%     +7.92%  (p=0.006 n=9+10)
    Intn-8                    3.86ns ± 1%    3.96ns ± 4%     +2.58%  (p=0.002 n=9+8)
    Intn_Big-8                6.46ns ± 2%    7.45ns ±15%     +15.35%  (p=0.000 n=8+10)
    Uint64-8                  3.06ns ± 1%    3.20ns ± 2%     +4.75%  (p=0.000 n=10+8)
    Rand_New                   107ns ± 1%     102ns ± 1%     -4.85%  (p=0.000 n=9+9)
    Rand_ExpFloat64           13.0ns ± 1%     8.9ns ± 3%     -31.25%  (p=0.000 n=9+8)
    Rand_Float32              7.67ns ± 3%     3.61ns ± 2%     -52.93%  (p=0.000 n=9+10)
    Rand_Float64              7.95ns ± 2%     4.22ns ± 2%     -46.99%  (p=0.000 n=10+8)
    Rand_Int                  6.79ns ± 1%     3.90ns ± 0%     -42.54%  (p=0.000 n=10+8)
    Rand_Int31                6.79ns ± 1%     2.56ns ± 1%     -62.30%  (p=0.000 n=10+9)
    Rand_Int31n               10.2ns ± 1%     4.2ns ± 0%      -59.15%  (p=0.000 n=9+9)
    Rand_Int31n_Big           10.2ns ± 1%     4.2ns ± 0%      -59.25%  (p=0.000 n=9+10)
    Rand_Int63                6.90ns ± 4%     3.92ns ± 1%     -43.20%  (p=0.000 n=9+8)
    Rand_Int63n               10.3ns ± 5%     5.5ns ± 0%      -46.40%  (p=0.000 n=9+8)
    Rand_Int63n_Big           28.8ns ± 1%     8.8ns ± 0%      -69.44%  (p=0.000 n=10+8)
    Rand_Intn                 10.2ns ± 1%     5.5ns ± 0%      -46.75%  (p=0.000 n=9+8)
    Rand_Intn_Big             28.7ns ± 1%     8.8ns ± 0%      -69.44%  (p=0.000 n=10+8)
    Rand_NormFloat64          14.1ns ± 2%     9.1ns ± 1%      -35.19%  (p=0.000 n=9+9)
    Rand_Perm                  951ns ± 1%     451ns ± 1%     -52.60%  (p=0.000 n=9+9)
    Rand_Shuffle               711ns ± 1%     412ns ± 1%     -42.12%  (p=0.000 n=10+8)
    Rand_ShuffleOverhead       638ns ± 1%     306ns ± 1%     -52.09%  (p=0.000 n=9+10)
    Rand_Uint32               7.12ns ±12%     2.48ns ± 1%     -65.24%  (p=0.000 n=10+9)
    Rand_Uint64               6.86ns ± 1%     3.93ns ± 0%     -42.64%  (p=0.000 n=8+9)
    ShuffleSlice               284ns ± 1%     278ns ± 1%     -2.09%  (p=0.000 n=8+8)
  9. Generate random integers with Rand

    master

    The Rand type provides several methods to generate uniformly distributed non-negative integers of various bit sizes and ranges:

    MethodReturn TypeDescription
    Uint32()uint32A 32-bit unsigned integer
    Uint32n(n uint32)uint32An unsigned integer in the range [0, n)
    Uint64()uint64A 64-bit unsigned integer
    Uint64n(n uint64)uint64An unsigned integer in the range [0, n)
    Int31()int32A 31-bit signed integer
    Int31n(n int32)int32A signed integer in the range [0, n)
    Int63()int64A 63-bit signed integer
    Int63n(n int64)int64A signed integer in the range [0, n)
    Intn(n int)intA signed integer in the range [0, n)
    Int()intA non-negative signed integer (up to math.MaxInt)

    Note: Methods like Int31n, Int63n, and Intn will panic if the argument n is less than or equal to 0.

    r := rand.New(1)
    
    // Range-bounded integers
    val32 := r.Uint32n(100) // [0, 100)
    val64 := r.Uint64n(1000) // [0, 1000)
    valInt := r.Intn(50)    // [0, 50)
  10. Generate random integers in a range [0, n)

    master

    To generate a random number within a specific range, use the n suffix functions. These functions panic if the provided limit n is less than or equal to 0 (except for Uint32n and Uint64n which return 0 if n is 0).

    • Intn(n int) int: Range [0, n).
    • Int31n(n int32) int32: Range [0, n).
    • Int63n(n int64) int64: Range [0, n).
    • Uint32n(n uint32) uint32: Range [0, n). Returns 0 if n is 0.
    • Uint64n(n uint64) uint64: Range [0, n). Returns 0 if n is 0.
    // Returns a value in [0, 10)
    val := rand.Intn(10)
    
    // Returns a uint32 in [0, 100)
    uval := rand.Uint32n(100)
  11. Fill a byte slice with random bytes

    master

    The Read(p []byte) function generates len(p) pseudo-random bytes and writes them into the slice p. It always returns len(p) and a nil error.

    bytes := make([]byte, 32)
    n, err := rand.Read(bytes)
    if err != nil {
        // handle error
    }
  12. Initialize a new Rand generator

    master

    Use New() to create a new pseudo-random number generator.

    • Non-deterministic: If you call New() with no arguments, the generator is initialized to a non-deterministic state.
    • Deterministic: If you provide a seed sequence (up to 3 uint64 values), the generator is initialized deterministically.

    Warning: New will panic if the provided seed sequence length is greater than 3.

    // Non-deterministic
    r := rand.New()
    
    // Deterministic with one seed
    r := rand.New(12345)
    
    // Deterministic with multiple seeds
    r := rand.New(1, 2, 3)