-
Notifications
You must be signed in to change notification settings - Fork 53
/
util_test.go
129 lines (115 loc) · 3.04 KB
/
util_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package lightstep
import (
gorand "math/rand"
"sync"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/lightstep/lightstep-tracer-go/lightstep/rand"
)
var _ = Describe("GenSeededGUID", func() {
Context("With a many calls on genSeededGUID", func() {
It("should not generate any duplicates", func() {
randompool = rand.NewPool(time.Now().UnixNano(), 10)
uniques := 1000000
ids := map[uint64]bool{}
for i := 0; i < uniques; i++ {
id := genSeededGUID()
ids[id] = true
}
Expect(len(ids)).To(Equal(uniques), "should have no duplicates")
})
})
Context("With a many calls on genSeededGUID2", func() {
It("should not generate any duplicates", func() {
randompool = rand.NewPool(time.Now().UnixNano(), 10)
uniques := 1000000
ids := map[uint64]bool{}
for i := 0; i < uniques; i++ {
id1, id2 := genSeededGUID2()
ids[id1] = true
ids[id2] = true
}
Expect(len(ids)).To(Equal(uniques*2), "should have no duplicates")
})
})
})
var _ = Measure("Single Source GenSeededGUID should handle concurrency badly", func(b Benchmarker) {
goroutines := 100
calls := 50000
b.Time("runtime", func() {
barrier := make(chan struct{})
group := &sync.WaitGroup{}
f := func(repeat int) {
<-barrier // block all goroutines
for i := 0; i < repeat; i++ {
_ = singleSourceGenSeededGUID()
}
group.Done()
}
for g := 0; g < goroutines; g++ {
group.Add(1)
go f(calls)
}
close(barrier) // lift the barrier
group.Wait()
})
}, 10)
var _ = Measure("Random Pool GenSeededGUID should handle concurrency efficiently", func(b Benchmarker) {
goroutines := 100
calls := 50000
randompool = rand.NewPool(time.Now().UnixNano(), 16)
b.Time("runtime", func() {
barrier := make(chan struct{})
group := &sync.WaitGroup{}
f := func(repeat int) {
<-barrier // block all goroutines
for i := 0; i < repeat; i++ {
_ = genSeededGUID()
}
group.Done()
}
for g := 0; g < goroutines; g++ {
group.Add(1)
go f(calls)
}
close(barrier) // lift the barrier
group.Wait()
})
}, 10)
var (
seededGUIDGen *gorand.Rand
seededGUIDGenOnce sync.Once
seededGUIDLock sync.Mutex
)
// implementation using single random generator
func singleSourceGenSeededGUID() uint64 {
// Golang does not seed the rng for us. Make sure it happens.
seededGUIDGenOnce.Do(func() {
seededGUIDGen = gorand.New(gorand.NewSource(time.Now().UnixNano()))
})
// The golang random generators are *not* intrinsically thread-safe.
seededGUIDLock.Lock()
defer seededGUIDLock.Unlock()
return uint64(seededGUIDGen.Int63())
}
func BenchmarkSingleSourceGenSeededGUID(b *testing.B) {
// run with 100000 goroutines
b.SetParallelism(100000)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
singleSourceGenSeededGUID()
}
})
}
func BenchmarkGenSeededRandomID(b *testing.B) {
// run with 100000 goroutines
b.SetParallelism(100000)
randompool = rand.NewPool(time.Now().UnixNano(), 16) // 16 random generators
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
genSeededGUID()
}
})
}