github.com/loov/hrtime@v1.0.3/stopwatchtsc.go (about) 1 package hrtime 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 ) 8 9 // SpanTSC defines a Count span 10 type SpanTSC struct { 11 Start Count 12 Finish Count 13 } 14 15 // ApproxDuration returns the approximate duration of the span. 16 func (span *SpanTSC) ApproxDuration() time.Duration { return span.Count().ApproxDuration() } 17 18 // Count returns the duration in count of the count span. 19 func (span *SpanTSC) Count() Count { return span.Finish - span.Start } 20 21 // StopwatchTSC allows concurrent benchmarking using TSC 22 type StopwatchTSC struct { 23 nextLap int32 24 lapsMeasured int32 25 spans []SpanTSC 26 wait sync.Mutex 27 } 28 29 // NewStopwatchTSC creates a new concurrent benchmark using TSC 30 func NewStopwatchTSC(count int) *StopwatchTSC { 31 if count <= 0 { 32 panic("must have count at least 1") 33 } 34 35 bench := &StopwatchTSC{ 36 nextLap: 0, 37 spans: make([]SpanTSC, count), 38 } 39 // lock mutex to ensure Wait() blocks until finalize is called 40 bench.wait.Lock() 41 return bench 42 } 43 44 // mustBeCompleted checks whether measurement has been completed. 45 func (bench *StopwatchTSC) mustBeCompleted() { 46 if int(atomic.LoadInt32(&bench.lapsMeasured)) < len(bench.spans) { 47 panic("benchmarking incomplete") 48 } 49 } 50 51 // Start starts measuring a new lap. 52 // It returns the lap number to pass in for Stop. 53 // It will return -1, when all measurements have been made. 54 // 55 // Call to Stop with -1 is ignored. 56 func (bench *StopwatchTSC) Start() int32 { 57 lap := atomic.AddInt32(&bench.nextLap, 1) - 1 58 if int(lap) > len(bench.spans) { 59 return -1 60 } 61 bench.spans[lap].Start = TSC() 62 return lap 63 } 64 65 // Stop stops measuring the specified lap. 66 // 67 // Call to Stop with -1 is ignored. 68 func (bench *StopwatchTSC) Stop(lap int32) { 69 if lap < 0 { 70 return 71 } 72 bench.spans[lap].Finish = TSC() 73 74 lapsMeasured := atomic.AddInt32(&bench.lapsMeasured, 1) 75 if int(lapsMeasured) == len(bench.spans) { 76 bench.finalize() 77 } else if int(lapsMeasured) > len(bench.spans) { 78 panic("stop called too many times") 79 } 80 } 81 82 // finalize finalizes the stopwatchTSC 83 func (bench *StopwatchTSC) finalize() { 84 // release the initial lock such that Wait can proceed. 85 bench.wait.Unlock() 86 } 87 88 // Wait waits for all measurements to be completed. 89 func (bench *StopwatchTSC) Wait() { 90 // lock waits for finalize to be called by the last measurement. 91 bench.wait.Lock() 92 _ = 1 // intentionally empty block, suppress staticcheck SA2001 warning 93 bench.wait.Unlock() 94 } 95 96 // Spans returns measured time-spans. 97 func (bench *StopwatchTSC) Spans() []SpanTSC { 98 bench.mustBeCompleted() 99 return append(bench.spans[:0:0], bench.spans...) 100 } 101 102 // ApproxDurations returns measured durations. 103 func (bench *StopwatchTSC) ApproxDurations() []time.Duration { 104 bench.mustBeCompleted() 105 106 durations := make([]time.Duration, len(bench.spans)) 107 for i, span := range bench.spans { 108 durations[i] = span.ApproxDuration() 109 } 110 111 return durations 112 } 113 114 // Name returns name of the benchmark. 115 func (bench *StopwatchTSC) Name() string { return "" } 116 117 // Unit returns units it measures. 118 func (bench *StopwatchTSC) Unit() string { return "tsc" } 119 120 // Float64s returns all measurements. 121 func (bench *StopwatchTSC) Float64s() []float64 { 122 measurements := make([]float64, len(bench.spans)) 123 for i := range measurements { 124 measurements[i] = float64(bench.spans[i].Count()) 125 } 126 return measurements 127 } 128 129 // Histogram creates an histogram of all the durations. 130 // 131 // It creates binCount bins to distribute the data and uses the 132 // 99.9 percentile as the last bucket range. However, for a nicer output 133 // it might choose a larger value. 134 func (bench *StopwatchTSC) Histogram(binCount int) *Histogram { 135 bench.mustBeCompleted() 136 137 opts := defaultOptions 138 opts.BinCount = binCount 139 140 return NewDurationHistogram(bench.ApproxDurations(), &opts) 141 } 142 143 // HistogramClamp creates an historgram of all the durations clamping minimum and maximum time. 144 // 145 // It creates binCount bins to distribute the data and uses the 146 // maximum as the last bucket. 147 func (bench *StopwatchTSC) HistogramClamp(binCount int, min, max time.Duration) *Histogram { 148 bench.mustBeCompleted() 149 150 durations := make([]time.Duration, 0, len(bench.spans)) 151 for _, span := range bench.spans { 152 duration := span.ApproxDuration() 153 if duration < min { 154 durations = append(durations, min) 155 } else { 156 durations = append(durations, duration) 157 } 158 } 159 160 opts := defaultOptions 161 opts.BinCount = binCount 162 opts.ClampMaximum = float64(max.Nanoseconds()) 163 opts.ClampPercentile = 0 164 165 return NewDurationHistogram(durations, &opts) 166 }