github.com/MeteorsLiu/rand@v0.0.0-20230523094032-b55484ce1d5d/readme.md (about) 1 # An optimized math/rand 2 3 Concurrency safe math/rand, optimized for high concurrent pseudo number generating. 4 5 # Why? 6 Go's random algorithm is great, its pseudo randomness behaves well. 7 8 However, the standard library uses a global mutex lock to ensure its safety of concurrency. 9 10 That's not a good things. 11 12 In a high concorrent pseudo number generating case, like a online game, the global mutex will increase the latency of requests when the parallel requests is incrasing. 13 14 ## How to solve? 15 16 It's easy to avoid the lock. 17 18 1. Pool 19 2. Atomic operation 20 21 In this porject, I choose to use the pool. The other one([Wyhash](https://github.com/MeteorsLiu/wyhash)) uses Atomic Operation. 22 23 However, if only one goroutine is using, there's no necessity to use the pool, because pulling out the pool and pushing into the pool cost, it's not free. 24 25 26 So I use a CAS lock to check whether only one goroutine uses it. 27 28 If yes, use a global lockless one.No, grab it from the pool. 29 30 31 # What's new? 32 33 I not only optimize the performance of concurrency, but also add some new things. 34 35 Like Do(), Intrange(), Uniform(), Sample(), ReadBytes(), Triangular()... 36 37 They are quite similar with the Python Standard libray. 38 39 Exclude Do(). 40 41 What does Do use for? 42 43 In some cases, we might call Int(), Intn() multiple times. 44 45 Like 46 ``` 47 n := rand.Intn(5) 48 for i :=0; i < n; i++ { 49 randseed := rand.Int() 50 } 51 ``` 52 53 If using a global mutex, locking and unlocking cost too much, that's not good. 54 55 However, if we can get a lockless one from the pool, and generate it multiple times. 56 57 There's no cost of locks. 58 59 That's the Do() do. 60 61 ``` 62 Do(func (r *rand.Rand) { 63 // No more lock here 64 n := r.Intn(5) 65 for i :=0; i < n; i++ { 66 randseed := r.Int() 67 } 68 }) 69 70 ``` 71 72 73 # Benchmark 74 75 ``` 76 goos: windows 77 goarch: amd64 78 pkg: github.com/MeteorsLiu/lockfree-rand 79 cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz 80 BenchmarkInt-8 247086846 4.794 ns/op 0 B/op 0 allocs/op 81 BenchmarkGoInt-8 73754803 16.29 ns/op 0 B/op 0 allocs/op 82 BenchmarkReadSmall-8 1595437 759.7 ns/op 0 B/op 0 allocs/op 83 BenchmarkGoReadSmall-8 1356376 887.3 ns/op 0 B/op 0 allocs/op 84 BenchmarkReadMedium-8 49314 24041 ns/op 0 B/op 0 allocs/op 85 BenchmarkGoReadMedium-8 43911 27435 ns/op 0 B/op 0 allocs/op 86 BenchmarkReadLarge-8 12649 97187 ns/op 0 B/op 0 allocs/op 87 BenchmarkGoReadLarge-8 10000 110534 ns/op 0 B/op 0 allocs/op 88 BenchmarkParallel-8 4934991 253.0 ns/op 16 B/op 1 allocs/op 89 BenchmarkGoParallel-8 4218584 282.6 ns/op 16 B/op 1 allocs/op 90 BenchmarkParallelRead-8 4548276 258.9 ns/op 16 B/op 1 allocs/op 91 BenchmarkGoParallelRead-8 2720745 443.8 ns/op 17 B/op 1 allocs/op 92 BenchmarkWyhashParallelRead-8 3443152 341.4 ns/op 24 B/op 1 allocs/op 93 BenchmarkWyhashPoolParallelRead-8 3987759 300.0 ns/op 48 B/op 1 allocs/op 94 BenchmarkGoMultipleDo-8 1210926 995.7 ns/op 18 B/op 1 allocs/op 95 BenchmarkMultipleDo-8 4430743 276.3 ns/op 16 B/op 1 allocs/op 96 ``` 97 The prefix of BenchmarkGo stands for **math/rand** function, without which, it stands for this repo. 98 99 Optimized for high concurrent cases, I use sync.Pool to avoid the global mutex lock.