-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcounter_test.go
149 lines (129 loc) · 3.11 KB
/
counter_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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package counter_test
import (
"testing"
"github.com/jldec/counter-go"
)
func TestCounter(t *testing.T) {
t.Run("Atomic", func(t *testing.T) {
test1(new(counter.CounterAtomic), t)
})
t.Run("Mutex", func(t *testing.T) {
test1(new(counter.CounterMutex), t)
})
t.Run("Channel", func(t *testing.T) {
test1(counter.NewCounterChannel(), t)
})
}
func TestCounterChannelPanicGet(t *testing.T) {
defer func() {
r := recover()
if r == nil {
t.Errorf("CounterChannel failed to panic on uninitialized Get().")
}
}()
cnt := new(counter.CounterChannel)
cnt.Get()
}
func TestCounterChannelPanicInc(t *testing.T) {
defer func() {
r := recover()
if r == nil {
t.Errorf("CounterChannel failed to panic on uninitialized Inc().")
}
}()
cnt := new(counter.CounterChannel)
cnt.Inc()
}
// basic counter test - not concurrent
func test1(cnt counter.Counter, t *testing.T) {
val := cnt.Get()
if val != 0 {
t.Errorf("Inital value: %d\nexpected: 0", val)
}
cnt.Inc()
cnt.Inc()
cnt.Inc()
val = cnt.Get()
if val != 3 {
t.Errorf("After 3x Inc() value: %d\nexpected: 3", val)
}
val = cnt.Get()
if val != 3 {
t.Errorf("After 3x Inc() + 1 Get() value: %d\nexpected: 3", val)
}
}
// Simple: 1 op = 1 Inc() in same thread
func BenchmarkCounter_1(b *testing.B) {
b.Run("Atomic", func(b *testing.B) {
bench1(new(counter.CounterAtomic), b)
})
b.Run("Mutex", func(b *testing.B) {
bench1(new(counter.CounterMutex), b)
})
b.Run("Channel", func(b *testing.B) {
bench1(counter.NewCounterChannel(), b)
})
}
func bench1(cnt counter.Counter, b *testing.B) {
i := 0
for i < b.N {
cnt.Inc()
i++
}
// b.Logf("%d iterations, counter = %d", i, cnt.Get())
}
// 1 op = 1 Inc() x 10 goroutines
// 1 op = [ 1 Inc() + 10 Get() ] x 10 goroutines
func BenchmarkCounter_2(b *testing.B) {
b.Run("Atomic no reads", func(b *testing.B) {
bench2(new(counter.CounterAtomic), 0, b)
})
b.Run("Mutex no reads", func(b *testing.B) {
bench2(new(counter.CounterMutex), 0, b)
})
b.Run("Channel no reads", func(b *testing.B) {
bench2(counter.NewCounterChannel(), 0, b)
})
b.Run("Atomic 10 reads", func(b *testing.B) {
bench2(new(counter.CounterAtomic), 10, b)
})
b.Run("Mutex 10 reads", func(b *testing.B) {
bench2(new(counter.CounterMutex), 10, b)
})
b.Run("Channel 10 reads", func(b *testing.B) {
bench2(counter.NewCounterChannel(), 10, b)
})
}
// Concurrent counter benchmark
func bench2(cnt counter.Counter, readRatio int, b *testing.B) {
done := make(chan int)
const GONUM = 10 // number of goroutines
for n := 0; n < GONUM; n++ {
go func(n int) {
var sumReads uint64 = 0
for i := 0; i < b.N; i++ {
cnt.Inc()
if readRatio > 0 {
for j := 0; j < readRatio; j++ {
sumReads += cnt.Get()
}
}
}
done <- n
}(n)
}
// Collect goroutine completion order and counter values.
type result struct {
n int
count uint64
}
var order [GONUM]result
for n := 0; n < GONUM; n++ {
order[n] = result{<-done, cnt.Get()}
}
val := cnt.Get()
if uint64(b.N*GONUM) != val {
b.Errorf("Concurrent counter expected %d\ngot %d", b.N*GONUM, val)
}
// b.Logf("%d iterations, order %v", b.N, order)
}