github.com/haraldrudell/parl@v0.4.176/prand/rand_bench_test.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package prand 7 8 import ( 9 cryptorand "crypto/rand" 10 "math/rand" 11 "testing" 12 "time" 13 "unsafe" 14 15 "github.com/haraldrudell/parl/ptesting" 16 ) 17 18 // How to generate pseudo-random numbers efficiently? 19 // - use fastrand 2.092 ns 20 // - create thread-local math/rand.Rand and then use Uint32: 2.240 ns 21 // - pick a slow option 22 23 // go test -run=^# -bench=BenchmarkRand32 ./ptesting 24 // 5 s 25 // 26 // goversion: go1.20.1 27 // osversion: macOS 13.2.1 28 // goos: darwin 29 // goarch: arm64 30 // pkg: github.com/haraldrudell/parl/ptesting 31 // BenchmarkRand32/math/rand.Uint32-10 84712857 14.19 ns/op 0 B/op 0 allocs/op 32 // BenchmarkRand32/crypto/rand.Read_32-bit-10 3601936 329.7 ns/op 0 B/op 0 allocs/op 33 // BenchmarkRand32/ptesting.Uint32-10 566568112 2.162 ns/op 0 B/op 0 allocs/op 34 func BenchmarkRand32(b *testing.B) { 35 36 println(ptesting.Versions()) 37 38 // math/rand.Uint32: thread-safe 39 b.Run("math/rand.Uint32", func(b *testing.B) { 40 for iteration := 0; iteration < b.N; iteration++ { 41 rand.Uint32() 42 } 43 }) 44 45 // math/rand.Uint32: thread-safe 46 b.Run("math/rand.Rand.Uint32", func(b *testing.B) { 47 // random generator with thread-local seed 48 var r = rand.New(rand.NewSource(0)) 49 for iteration := 0; iteration < b.N; iteration++ { 50 r.Uint32() 51 } 52 }) 53 54 // crypto/rand.Read: thread-safe 55 b.Run("crypto/rand.Read 32-bit", func(b *testing.B) { 56 var length = unsafe.Sizeof(uint32(1)) 57 var byts = make([]byte, length) 58 b.ResetTimer() 59 for iteration := 0; iteration < b.N; iteration++ { 60 cryptorand.Read(byts) 61 } 62 }) 63 64 // runtime.fastrand 32-bit: thread-safe 65 b.Run("ptesting.Uint32", func(b *testing.B) { 66 for iteration := 0; iteration < b.N; iteration++ { 67 Uint32() 68 } 69 }) 70 } 71 72 // go test -run=^# -bench=BenchmarkRand64 ./ptesting 73 // 5 s 74 // 75 // goversion: go1.20.1 76 // osversion: macOS 13.2.1 77 // goos: darwin 78 // goarch: arm64 79 // pkg: github.com/haraldrudell/parl/ptesting 80 // BenchmarkRand64/math/rand.Uint64-10 84433459 14.01 ns/op 0 B/op 0 allocs/op 81 // BenchmarkRand64/crypto/rand.Read_64-bit-10 3783060 323.8 ns/op 0 B/op 0 allocs/op 82 // BenchmarkRand64/ptesting.Uint64-10 277811217 4.289 ns/op 0 B/op 0 allocs/op 83 func BenchmarkRand64(b *testing.B) { 84 85 println(ptesting.Versions()) 86 87 // math/rand.Uint64: thread-safe 88 b.Run("math/rand.Uint64", func(b *testing.B) { 89 for iteration := 0; iteration < b.N; iteration++ { 90 rand.Uint64() 91 } 92 }) 93 94 // crypto/rand.Read: thread-safe 95 b.Run("crypto/rand.Read 64-bit", func(b *testing.B) { 96 var length = unsafe.Sizeof(uint64(1)) 97 var byts = make([]byte, length) 98 b.ResetTimer() 99 for iteration := 0; iteration < b.N; iteration++ { 100 cryptorand.Read(byts) 101 } 102 }) 103 104 // runtime.fastrand 64-bit: thread-safe 105 b.Run("ptesting.Uint64", func(b *testing.B) { 106 for iteration := 0; iteration < b.N; iteration++ { 107 Uint64() 108 } 109 }) 110 } 111 112 // thread-local math/rand.Rand.Uint32 pseudo-random number on 2021 M1 Max is 2.240 ns 113 // - thread must have Rand available 114 // 115 // Running tool: /opt/homebrew/bin/go test -benchmem -run=^$ -bench ^BenchmarkNewRand$ github.com/haraldrudell/parl/prand 116 // goos: darwin 117 // goarch: arm64 118 // pkg: github.com/haraldrudell/parl/prand 119 // BenchmarkNewRand-10 53342386 22.40 ns/op 2.240 wall-ns/op 0 B/op 0 allocs/op 120 // PASS 121 // ok github.com/haraldrudell/parl/prand 2.234s 122 func BenchmarkNewRand(b *testing.B) { 123 const uint32Count = 10 124 var r = rand.New(rand.NewSource(time.Now().UnixNano())) 125 b.ResetTimer() 126 for i := 0; i < b.N; i++ { 127 r.Uint32() // 1 128 r.Uint32() // 2 129 r.Uint32() // 3 130 r.Uint32() // 4 131 r.Uint32() // 5 132 r.Uint32() // 6 133 r.Uint32() // 7 134 r.Uint32() // 8 135 r.Uint32() // 9 136 r.Uint32() // 10 137 } 138 b.StopTimer() 139 // elapsed is duration of 10 fastrand invocations. Unit ns, int64 140 var elapsed = b.Elapsed() 141 // nUnit is wall-time nanoseconds per fastrand invocation 142 // - float64, unit ns 143 var nUnit float64 = // 144 float64(elapsed) / 145 float64(b.N) / 146 uint32Count 147 b.ReportMetric(nUnit, "wall-ns/op") 148 } 149 150 // time.Now().UnixNano() as 64-bit pseudo-random number on 2021 M1 Max is 38.00 ns 151 // 152 // 231114 c66 153 // Running tool: /opt/homebrew/bin/go test -benchmem -run=^$ -bench ^BenchmarkTimeNow$ github.com/haraldrudell/parl/prand 154 // goos: darwin 155 // goarch: arm64 156 // pkg: github.com/haraldrudell/parl/prand 157 // BenchmarkTimeNow-10 31434660 38.00 ns/op 0 B/op 0 allocs/op 158 // PASS 159 // ok github.com/haraldrudell/parl/prand 2.325s 160 func BenchmarkTimeNow(b *testing.B) { 161 for i := 0; i < b.N; i++ { 162 time.Now().UnixNano() 163 } 164 } 165 166 // fastrand 32-bit pseudo-random number on 2021 M1 Max is 2.092 ns 167 // 168 // 231114 c66 169 // Running tool: /opt/homebrew/bin/go test -benchmem -run=^$ -bench ^BenchmarkFastRand$ github.com/haraldrudell/parl/prand 170 // goos: darwin 171 // goarch: arm64 172 // pkg: github.com/haraldrudell/parl/prand 173 // BenchmarkFastRand-10 57573570 20.92 ns/op 2.092 wall-ns/op 0 B/op 0 allocs/op 174 // PASS 175 // ok github.com/haraldrudell/parl/prand 2.288s 176 func BenchmarkFastRand(b *testing.B) { 177 const fastRandCount = 10 178 for i := 0; i < b.N; i++ { 179 // fastrand invocation is 2.039 ns which is too short to be tested by itself 180 // - at least 4 ns of measured task is required for metric quality 181 // - 10 fastrand is 21.10 ns 182 fastrand() // 1 183 fastrand() // 2 184 fastrand() // 3 185 fastrand() // 4 186 fastrand() // 5 187 fastrand() // 6 188 fastrand() // 7 189 fastrand() // 8 190 fastrand() // 9 191 fastrand() // 10 192 } 193 b.StopTimer() 194 // elapsed is duration of 10 fastrand invocations. Unit ns, int64 195 var elapsed = b.Elapsed() 196 // nUnit is wall-time nanoseconds per fastrand invocation 197 // - float64, unit ns 198 var nUnit float64 = // 199 float64(elapsed) / 200 float64(b.N) / 201 fastRandCount 202 b.ReportMetric(nUnit, "wall-ns/op") 203 }