pax_global_header00006660000000000000000000000064141432113240014505gustar00rootroot0000000000000052 comment=ace71649eead7268a82fe6d5492f56cef039dec8 golang-github-valyala-fastrand-1.1.0/000077500000000000000000000000001414321132400174625ustar00rootroot00000000000000golang-github-valyala-fastrand-1.1.0/.travis.yml000066400000000000000000000003611414321132400215730ustar00rootroot00000000000000language: go go: - 1.7 - 1.8 script: # build test for supported platforms - GOOS=linux go build - GOOS=darwin go build - GOOS=freebsd go build - GOARCH=386 go build # run tests on a standard platform - go test -v ./... golang-github-valyala-fastrand-1.1.0/LICENSE000066400000000000000000000020771414321132400204750ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Aliaksandr Valialkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-valyala-fastrand-1.1.0/README.md000066400000000000000000000054001414321132400207400ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/valyala/fastrand.svg)](https://travis-ci.org/valyala/fastrand) [![GoDoc](https://godoc.org/github.com/valyala/fastrand?status.svg)](http://godoc.org/github.com/valyala/fastrand) [![Go Report](https://goreportcard.com/badge/github.com/valyala/fastrand)](https://goreportcard.com/report/github.com/valyala/fastrand) # fastrand Fast pseudorandom number generator. # Features - Optimized for speed. - Performance scales on multiple CPUs. # How does it work? It abuses [sync.Pool](https://golang.org/pkg/sync/#Pool) for maintaining "per-CPU" pseudorandom number generators. TODO: firgure out how to use real per-CPU pseudorandom number generators. # Benchmark results ``` $ GOMAXPROCS=1 go test -bench=. github.com/valyala/fastrand goos: linux goarch: amd64 pkg: github.com/valyala/fastrand BenchmarkUint32n 50000000 29.7 ns/op BenchmarkRNGUint32n 200000000 6.50 ns/op BenchmarkRNGUint32nWithLock 100000000 21.5 ns/op BenchmarkMathRandInt31n 50000000 31.8 ns/op BenchmarkMathRandRNGInt31n 100000000 17.9 ns/op BenchmarkMathRandRNGInt31nWithLock 50000000 30.2 ns/op PASS ok github.com/valyala/fastrand 10.634s ``` ``` $ GOMAXPROCS=2 go test -bench=. github.com/valyala/fastrand goos: linux goarch: amd64 pkg: github.com/valyala/fastrand BenchmarkUint32n-2 100000000 17.6 ns/op BenchmarkRNGUint32n-2 500000000 3.36 ns/op BenchmarkRNGUint32nWithLock-2 50000000 32.0 ns/op BenchmarkMathRandInt31n-2 20000000 51.2 ns/op BenchmarkMathRandRNGInt31n-2 100000000 11.0 ns/op BenchmarkMathRandRNGInt31nWithLock-2 20000000 91.0 ns/op PASS ok github.com/valyala/fastrand 9.543s ``` ``` $ GOMAXPROCS=4 go test -bench=. github.com/valyala/fastrand goos: linux goarch: amd64 pkg: github.com/valyala/fastrand BenchmarkUint32n-4 100000000 14.2 ns/op BenchmarkRNGUint32n-4 500000000 3.30 ns/op BenchmarkRNGUint32nWithLock-4 20000000 88.7 ns/op BenchmarkMathRandInt31n-4 10000000 145 ns/op BenchmarkMathRandRNGInt31n-4 200000000 8.35 ns/op BenchmarkMathRandRNGInt31nWithLock-4 20000000 102 ns/op PASS ok github.com/valyala/fastrand 11.534s ``` As you can see, [fastrand.Uint32n](https://godoc.org/github.com/valyala/fastrand#Uint32n) scales on multiple CPUs, while [rand.Int31n](https://golang.org/pkg/math/rand/#Int31n) doesn't scale. Their performance is comparable on `GOMAXPROCS=1`, but `fastrand.Uint32n` runs 3x faster than `rand.Int31n` on `GOMAXPROCS=2` and 10x faster than `rand.Int31n` on `GOMAXPROCS=4`. golang-github-valyala-fastrand-1.1.0/fastrand.go000066400000000000000000000033711414321132400216170ustar00rootroot00000000000000// Package fastrand implements fast pesudorandom number generator // that should scale well on multi-CPU systems. // // Use crypto/rand instead of this package for generating // cryptographically secure random numbers. package fastrand import ( "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 // Uint32n returns pseudorandom uint32 in the range [0..maxN). // // It is safe calling this function from concurrent goroutines. func Uint32n(maxN uint32) uint32 { x := Uint32() // See http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return uint32((uint64(x) * uint64(maxN)) >> 32) } // RNG is a pseudorandom number generator. // // It is unsafe to call RNG methods from concurrent goroutines. type RNG struct { x uint32 } // Uint32 returns pseudorandom uint32. // // 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 x ^= x >> 17 x ^= x << 5 r.x = x return x } // Uint32n returns pseudorandom uint32 in the range [0..maxN). // // It is unsafe to call this method from concurrent goroutines. func (r *RNG) Uint32n(maxN uint32) uint32 { x := r.Uint32() // See http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return uint32((uint64(x) * uint64(maxN)) >> 32) } // Seed sets the r state to n. func (r *RNG) Seed(n uint32) { r.x = n } func getRandomUint32() uint32 { x := time.Now().UnixNano() return uint32((x >> 32) ^ x) } golang-github-valyala-fastrand-1.1.0/fastrand_test.go000066400000000000000000000030611414321132400226520ustar00rootroot00000000000000package fastrand import ( "testing" ) func TestRNGSeed(t *testing.T) { var r RNG for _, seed := range []uint32{1, 2, 1234, 432432, 34324432} { r.Seed(seed) m := make(map[uint32]struct{}) for i := 0; i < 1e6; i++ { n := r.Uint32() if _, ok := m[n]; ok { t.Fatalf("number %v already exists", n) } m[n] = struct{}{} } } } func TestUint32(t *testing.T) { m := make(map[uint32]struct{}) for i := 0; i < 1e6; i++ { n := Uint32() if _, ok := m[n]; ok { t.Fatalf("number %v already exists", n) } m[n] = struct{}{} } } func TestRNGUint32(t *testing.T) { var r RNG m := make(map[uint32]struct{}) for i := 0; i < 1e6; i++ { n := r.Uint32() if _, ok := m[n]; ok { t.Fatalf("number %v already exists", n) } m[n] = struct{}{} } } func TestUint32n(t *testing.T) { m := make(map[uint32]int) for i := 0; i < 1e6; i++ { n := Uint32n(1e2) if n >= 1e2 { t.Fatalf("n > 1000: %v", n) } m[n]++ } // check distribution avg := 1e6 / 1e2 for k, v := range m { p := (float64(v) - float64(avg)) / float64(avg) if p < 0 { p = -p } if p > 0.05 { t.Fatalf("skew more than 5%% for k=%v: %v", k, p*100) } } } func TestRNGUint32n(t *testing.T) { var r RNG m := make(map[uint32]int) for i := 0; i < 1e6; i++ { n := r.Uint32n(1e2) if n >= 1e2 { t.Fatalf("n > 1000: %v", n) } m[n]++ } // check distribution avg := 1e6 / 1e2 for k, v := range m { p := (float64(v) - float64(avg)) / float64(avg) if p < 0 { p = -p } if p > 0.05 { t.Fatalf("skew more than 5%% for k=%v: %v", k, p*100) } } } golang-github-valyala-fastrand-1.1.0/fastrand_timing_test.go000066400000000000000000000047451414321132400242330ustar00rootroot00000000000000package fastrand import ( "math/rand" "sync" "sync/atomic" "testing" "unsafe" ) // BenchSink prevents the compiler from optimizing away benchmark loops. var BenchSink uint32 func BenchmarkUint32n(b *testing.B) { b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { s += Uint32n(1e6) } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkRNGUint32n(b *testing.B) { b.RunParallel(func(pb *testing.PB) { var r RNG s := uint32(0) for pb.Next() { s += r.Uint32n(1e6) } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkRNGUint32nWithLock(b *testing.B) { var r RNG var rMu sync.Mutex b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { rMu.Lock() s += r.Uint32n(1e6) rMu.Unlock() } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkRNGUint32nArray(b *testing.B) { var rr [64]struct { r RNG mu sync.Mutex // pad prevents from false sharing pad [64 - (unsafe.Sizeof(RNG{})+unsafe.Sizeof(sync.Mutex{}))%64]byte } var n uint32 b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { idx := atomic.AddUint32(&n, 1) r := &rr[idx%uint32(len(rr))] r.mu.Lock() s += r.r.Uint32n(1e6) r.mu.Unlock() } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkMathRandInt31n(b *testing.B) { b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { s += uint32(rand.Int31n(1e6)) } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkMathRandRNGInt31n(b *testing.B) { b.RunParallel(func(pb *testing.PB) { r := rand.New(rand.NewSource(42)) s := uint32(0) for pb.Next() { s += uint32(r.Int31n(1e6)) } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkMathRandRNGInt31nWithLock(b *testing.B) { r := rand.New(rand.NewSource(42)) var rMu sync.Mutex b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { rMu.Lock() s += uint32(r.Int31n(1e6)) rMu.Unlock() } atomic.AddUint32(&BenchSink, s) }) } func BenchmarkMathRandRNGInt31nArray(b *testing.B) { var rr [64]struct { r *rand.Rand mu sync.Mutex // pad prevents from false sharing pad [64 - (unsafe.Sizeof(RNG{})+unsafe.Sizeof(sync.Mutex{}))%64]byte } for i := range rr { rr[i].r = rand.New(rand.NewSource(int64(i))) } var n uint32 b.RunParallel(func(pb *testing.PB) { s := uint32(0) for pb.Next() { idx := atomic.AddUint32(&n, 1) r := &rr[idx%uint32(len(rr))] r.mu.Lock() s += uint32(r.r.Int31n(1e6)) r.mu.Unlock() } atomic.AddUint32(&BenchSink, s) }) } golang-github-valyala-fastrand-1.1.0/go.mod000066400000000000000000000000431414321132400205650ustar00rootroot00000000000000module github.com/valyala/fastrand