Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package mathrand | 5 package mathrand |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "math" | 8 "math" |
| 9 "math/rand" | 9 "math/rand" |
| 10 "testing" | 10 "testing" |
| 11 | 11 |
| 12 . "github.com/smartystreets/goconvey/convey" | 12 . "github.com/smartystreets/goconvey/convey" |
| 13 "golang.org/x/net/context" | 13 "golang.org/x/net/context" |
| 14 ) | 14 ) |
| 15 | 15 |
| 16 func Test(t *testing.T) { | 16 func Test(t *testing.T) { |
| 17 t.Parallel() | 17 t.Parallel() |
| 18 | 18 |
| 19 Convey("test mathrand", t, func() { | 19 Convey("test mathrand", t, func() { |
| 20 c := context.Background() | 20 c := context.Background() |
| 21 | 21 |
| 22 Convey("unset", func() { | 22 Convey("unset", func() { |
| 23 // Just ensure doesn't crash. | 23 // Just ensure doesn't crash. |
| 24 So(Get(c).Int()+1 > 0, ShouldBeTrue) | 24 So(Get(c).Int()+1 > 0, ShouldBeTrue) |
| 25 So(WithGoRand(c, func(r *rand.Rand) error { | |
| 26 So(r.Int(), ShouldBeGreaterThanOrEqualTo, 0) | |
| 27 return nil | |
| 28 }), ShouldBeNil) | |
| 25 }) | 29 }) |
| 26 | 30 |
| 27 Convey("set persistance", func() { | 31 Convey("set persistance", func() { |
| 28 c = Set(c, rand.New(rand.NewSource(12345))) | 32 c = Set(c, rand.New(rand.NewSource(12345))) |
| 29 r := rand.New(rand.NewSource(12345)) | 33 r := rand.New(rand.NewSource(12345)) |
| 30 So(Get(c).Int(), ShouldEqual, r.Int()) | 34 So(Get(c).Int(), ShouldEqual, r.Int()) |
| 31 So(Get(c).Int(), ShouldEqual, r.Int()) | 35 So(Get(c).Int(), ShouldEqual, r.Int()) |
| 32 }) | 36 }) |
| 33 | 37 |
| 34 Convey("nil set", func() { | 38 Convey("nil set", func() { |
| 35 c = Set(c, nil) | 39 c = Set(c, nil) |
| 36 // Just ensure doesn't crash. | 40 // Just ensure doesn't crash. |
| 37 So(Get(c).Int()+1 > 0, ShouldBeTrue) | 41 So(Get(c).Int()+1 > 0, ShouldBeTrue) |
| 38 }) | 42 }) |
| 39 }) | 43 }) |
| 40 | 44 |
| 41 Convey("fairness of uninitialized source", t, func() { | 45 Convey("fairness of uninitialized source", t, func() { |
| 42 // We do some ugly stuff in Get(...) if context doesn't have mat h.Rand set, | 46 // We do some ugly stuff in Get(...) if context doesn't have mat h.Rand set, |
| 43 // check that the produced RNG sequence matches the uniform dist ribution | 47 // check that the produced RNG sequence matches the uniform dist ribution |
| 44 // at least at first two moments. | 48 // at least at first two moments. |
| 45 ctx := context.Background() | 49 ctx := context.Background() |
| 46 » » mean, dev := calcStats(10000, func() float64 { | 50 » » mean, dev := calcStats(20000, func() float64 { |
| 47 return Get(ctx).Float64() | 51 return Get(ctx).Float64() |
| 48 }) | 52 }) |
| 49 | 53 |
| 50 // For ideal uniform [0, 1) distribution it should be: | 54 // For ideal uniform [0, 1) distribution it should be: |
| 51 // Average: 0.500000 | 55 // Average: 0.500000 |
| 52 // Standard deviation: 0.288675 | 56 // Standard deviation: 0.288675 |
| 53 So(mean, ShouldBeBetween, 0.495, 0.505) | 57 So(mean, ShouldBeBetween, 0.495, 0.505) |
| 54 So(dev, ShouldBeBetween, 0.284, 0.29) | 58 So(dev, ShouldBeBetween, 0.284, 0.29) |
| 55 }) | 59 }) |
| 56 } | 60 } |
| 57 | 61 |
| 62 func testConcurrentAccess(t *testing.T, r *rand.Rand) { | |
| 63 const goroutines = 16 | |
| 64 const rounds = 1024 | |
| 65 | |
| 66 Convey(`Concurrent access does not produce a race or deadlock.`, func() { | |
| 67 c := context.Background() | |
| 68 if r != nil { | |
| 69 c = Set(c, r) | |
| 70 } | |
| 71 | |
| 72 startC := make(chan struct{}) | |
| 73 doneC := make(chan struct{}, goroutines) | |
| 74 for g := 0; g < goroutines; g++ { | |
| 75 go func() { | |
| 76 defer func() { | |
| 77 doneC <- struct{}{} | |
| 78 }() | |
| 79 | |
| 80 <-startC | |
| 81 for i := 0; i < rounds; i++ { | |
| 82 Int(c) | |
| 83 } | |
| 84 }() | |
| 85 } | |
| 86 | |
| 87 close(startC) | |
| 88 for reap := 0; reap < goroutines; reap++ { | |
| 89 <-doneC | |
| 90 } | |
| 91 }) | |
| 92 | |
| 93 } | |
| 94 | |
| 95 // TestConcurrentGlobalAccess is intentionally NOT Parallel, since we want to | |
| 96 // have exclusive access to the global instance. | |
| 97 func TestConcurrentGlobalAccess(t *testing.T) { | |
| 98 Convey(`Testing concurrent global access`, t, func() { | |
| 99 testConcurrentAccess(t, nil) | |
| 100 }) | |
| 101 } | |
| 102 | |
| 103 func TestConcurrentAccess(t *testing.T) { | |
| 104 t.Parallel() | |
| 105 | |
| 106 Convey(`Testing concurrent non-global access`, t, func() { | |
| 107 testConcurrentAccess(t, newRand()) | |
| 108 }) | |
| 109 } | |
| 110 | |
| 58 func calcStats(n int, gen func() float64) (avg float64, std float64) { | 111 func calcStats(n int, gen func() float64) (avg float64, std float64) { |
| 59 var m1 float64 | 112 var m1 float64 |
| 60 var m2 float64 | 113 var m2 float64 |
| 61 | 114 |
| 62 for i := 0; i < n; i++ { | 115 for i := 0; i < n; i++ { |
| 63 x := gen() | 116 x := gen() |
| 64 m1 += x | 117 m1 += x |
| 65 m2 += x * x | 118 m2 += x * x |
| 66 } | 119 } |
| 67 | 120 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 103 ctx := context.Background() | 156 ctx := context.Background() |
| 104 WithGoRand(ctx, func(r *rand.Rand) error { | 157 WithGoRand(ctx, func(r *rand.Rand) error { |
| 105 ctx = Set(ctx, r) | 158 ctx = Set(ctx, r) |
| 106 calcStats(b.N, func() float64 { | 159 calcStats(b.N, func() float64 { |
| 107 return Float64(ctx) | 160 return Float64(ctx) |
| 108 }) | 161 }) |
| 109 return nil | 162 return nil |
| 110 }) | 163 }) |
| 111 } | 164 } |
| 112 | 165 |
| 113 // BenchmarkStdlibDefaultSource-8 » 50000000» 32.0 ns/ op | 166 func BenchmarkGlobalSource(b *testing.B) { |
| 114 // BenchmarkOurDefaultSourceViaCtx-8 » 200000» 10893 ns/ op | 167 » r, _ := getGlobalRand() |
| 115 // BenchmarkOurDefaultSourceViaFunc-8 » 30000000» 38.7 ns/ op | 168 » calcStats(b.N, func() float64 { |
| 116 // BenchmarkOurInitializedSourceViaCtx-8 » 50000000» 30.2 ns/ op | 169 » » return r.Float64() |
| 117 // BenchmarkOurInitializedSourceViaFunc-8 » 50000000» 29.1 ns/ op | 170 » }) |
| 171 } | |
| 172 | |
| 173 // BenchmarkStdlibDefaultSource-32 30000000 35.6 ns/op | |
| 174 // BenchmarkOurDefaultSourceViaCtx-32 20000000 77.8 ns/op | |
|
Vadim Sh.
2016/12/20 02:31:56
awesome!
dnj
2016/12/20 02:36:06
To be fair it's b/c I'm not doing an allocation pe
| |
| 175 // BenchmarkOurDefaultSourceViaFunc-32 20000000 78.6 ns/op | |
| 176 // BenchmarkOurInitializedSourceViaCtx-32 20000000 86.8 ns/op | |
| 177 // BenchmarkOurInitializedSourceViaFunc-32 20000000 81.9 ns/op | |
| 178 // BenchmarkGlobalSource-32 30000000 43.8 ns/op | |
| OLD | NEW |