Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

RNG seeding using crypto/rand uint64 #6

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 12 additions & 10 deletions fastrand.go
Expand Up @@ -6,25 +6,29 @@
package fastrand

import (
"crypto/rand"
"encoding/binary"
"sync"
"time"
)

// Uint32 returns pseudorandom uint32.
//
// It is safe calling this function from concurrent goroutines.
func Uint32() uint32 {
v := rngPool.Get()
if v == nil {
v = &RNG{}
}
r := v.(*RNG)
x := r.Uint32()
rngPool.Put(r)
return x
}

var rngPool sync.Pool
var rngPool = sync.Pool{
New: func() interface{} {
return &RNG{
x: getRandomUint32(),
}
},
}

// Uint32n returns pseudorandom uint32 in the range [0..maxN).
//
Expand All @@ -46,10 +50,6 @@ type RNG struct {
//
// It is unsafe to call this method from concurrent goroutines.
func (r *RNG) Uint32() uint32 {
for r.x == 0 {
r.x = getRandomUint32()
}

// See https://en.wikipedia.org/wiki/Xorshift
x := r.x
x ^= x << 13
Expand All @@ -69,6 +69,8 @@ func (r *RNG) Uint32n(maxN uint32) uint32 {
}

func getRandomUint32() uint32 {
x := time.Now().UnixNano()
b := make([]byte, 8)
_, _ = rand.Read(b)
x := binary.BigEndian.Uint64(b)
return uint32((x >> 32) ^ x)
}
25 changes: 19 additions & 6 deletions fastrand_test.go
Expand Up @@ -5,18 +5,29 @@ import (
)

func TestUint32(t *testing.T) {
m := make(map[uint32]struct{})
m := make(map[uint32]int)
for i := 0; i < 1e6; i++ {
n := Uint32()
if _, ok := m[n]; ok {
t.Fatalf("number %v already exists", n)
m[n]++
}

const minUniqueValues = 0.9 * 1e6
if len(m) < minUniqueValues {
t.Errorf("only %d unique numbers were generated", len(m))
}

const maxRepetition = 3
for number, count := range m {
if count > maxRepetition {
t.Errorf("number %d was generated %d times", number, count)
}
m[n] = struct{}{}
}
}

func TestRNGUint32(t *testing.T) {
var r RNG
r := RNG{
x: getRandomUint32(),
}
m := make(map[uint32]struct{})
for i := 0; i < 1e6; i++ {
n := r.Uint32()
Expand Down Expand Up @@ -51,7 +62,9 @@ func TestUint32n(t *testing.T) {
}

func TestRNGUint32n(t *testing.T) {
var r RNG
r := RNG{
x: getRandomUint32(),
}
m := make(map[uint32]int)
for i := 0; i < 1e6; i++ {
n := r.Uint32n(1e2)
Expand Down
6 changes: 6 additions & 0 deletions fastrand_timing_test.go
Expand Up @@ -128,3 +128,9 @@ func BenchmarkMathRandRNGInt31nArray(b *testing.B) {
atomic.AddUint32(&BenchSink, s)
})
}

func Benchmark_getRandomUint32(b *testing.B) {
for i := 0; i < b.N; i++ {
getRandomUint32()
}
}
2 changes: 2 additions & 0 deletions go.mod
@@ -1 +1,3 @@
module github.com/valyala/fastrand

go 1.16