-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathstopwatchtsc.go
166 lines (139 loc) · 4.34 KB
/
stopwatchtsc.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package hrtime
import (
"sync"
"sync/atomic"
"time"
)
// SpanTSC defines a Count span
type SpanTSC struct {
Start Count
Finish Count
}
// ApproxDuration returns the approximate duration of the span.
func (span *SpanTSC) ApproxDuration() time.Duration { return span.Count().ApproxDuration() }
// Count returns the duration in count of the count span.
func (span *SpanTSC) Count() Count { return span.Finish - span.Start }
// StopwatchTSC allows concurrent benchmarking using TSC
type StopwatchTSC struct {
nextLap int32
lapsMeasured int32
spans []SpanTSC
wait sync.Mutex
}
// NewStopwatchTSC creates a new concurrent benchmark using TSC
func NewStopwatchTSC(count int) *StopwatchTSC {
if count <= 0 {
panic("must have count at least 1")
}
bench := &StopwatchTSC{
nextLap: 0,
spans: make([]SpanTSC, count),
}
// lock mutex to ensure Wait() blocks until finalize is called
bench.wait.Lock()
return bench
}
// mustBeCompleted checks whether measurement has been completed.
func (bench *StopwatchTSC) mustBeCompleted() {
if int(atomic.LoadInt32(&bench.lapsMeasured)) < len(bench.spans) {
panic("benchmarking incomplete")
}
}
// Start starts measuring a new lap.
// It returns the lap number to pass in for Stop.
// It will return -1, when all measurements have been made.
//
// Call to Stop with -1 is ignored.
func (bench *StopwatchTSC) Start() int32 {
lap := atomic.AddInt32(&bench.nextLap, 1) - 1
if int(lap) > len(bench.spans) {
return -1
}
bench.spans[lap].Start = TSC()
return lap
}
// Stop stops measuring the specified lap.
//
// Call to Stop with -1 is ignored.
func (bench *StopwatchTSC) Stop(lap int32) {
if lap < 0 {
return
}
bench.spans[lap].Finish = TSC()
lapsMeasured := atomic.AddInt32(&bench.lapsMeasured, 1)
if int(lapsMeasured) == len(bench.spans) {
bench.finalize()
} else if int(lapsMeasured) > len(bench.spans) {
panic("stop called too many times")
}
}
// finalize finalizes the stopwatchTSC
func (bench *StopwatchTSC) finalize() {
// release the initial lock such that Wait can proceed.
bench.wait.Unlock()
}
// Wait waits for all measurements to be completed.
func (bench *StopwatchTSC) Wait() {
// lock waits for finalize to be called by the last measurement.
bench.wait.Lock()
_ = 1 // intentionally empty block, suppress staticcheck SA2001 warning
bench.wait.Unlock()
}
// Spans returns measured time-spans.
func (bench *StopwatchTSC) Spans() []SpanTSC {
bench.mustBeCompleted()
return append(bench.spans[:0:0], bench.spans...)
}
// ApproxDurations returns measured durations.
func (bench *StopwatchTSC) ApproxDurations() []time.Duration {
bench.mustBeCompleted()
durations := make([]time.Duration, len(bench.spans))
for i, span := range bench.spans {
durations[i] = span.ApproxDuration()
}
return durations
}
// Name returns name of the benchmark.
func (bench *StopwatchTSC) Name() string { return "" }
// Unit returns units it measures.
func (bench *StopwatchTSC) Unit() string { return "tsc" }
// Float64s returns all measurements.
func (bench *StopwatchTSC) Float64s() []float64 {
measurements := make([]float64, len(bench.spans))
for i := range measurements {
measurements[i] = float64(bench.spans[i].Count())
}
return measurements
}
// Histogram creates an histogram of all the durations.
//
// It creates binCount bins to distribute the data and uses the
// 99.9 percentile as the last bucket range. However, for a nicer output
// it might choose a larger value.
func (bench *StopwatchTSC) Histogram(binCount int) *Histogram {
bench.mustBeCompleted()
opts := defaultOptions
opts.BinCount = binCount
return NewDurationHistogram(bench.ApproxDurations(), &opts)
}
// HistogramClamp creates an historgram of all the durations clamping minimum and maximum time.
//
// It creates binCount bins to distribute the data and uses the
// maximum as the last bucket.
func (bench *StopwatchTSC) HistogramClamp(binCount int, min, max time.Duration) *Histogram {
bench.mustBeCompleted()
durations := make([]time.Duration, 0, len(bench.spans))
for _, span := range bench.spans {
duration := span.ApproxDuration()
if duration < min {
durations = append(durations, min)
} else {
durations = append(durations, duration)
}
}
opts := defaultOptions
opts.BinCount = binCount
opts.ClampMaximum = float64(max.Nanoseconds())
opts.ClampPercentile = 0
return NewDurationHistogram(durations, &opts)
}