1package leafnodes
2
3import (
4	"math"
5	"time"
6
7	"sync"
8
9	"github.com/onsi/ginkgo/types"
10)
11
12type benchmarker struct {
13	mu           sync.Mutex
14	measurements map[string]*types.SpecMeasurement
15	orderCounter int
16}
17
18func newBenchmarker() *benchmarker {
19	return &benchmarker{
20		measurements: make(map[string]*types.SpecMeasurement),
21	}
22}
23
24func (b *benchmarker) Time(name string, body func(), info ...interface{}) (elapsedTime time.Duration) {
25	t := time.Now()
26	body()
27	elapsedTime = time.Since(t)
28
29	b.mu.Lock()
30	defer b.mu.Unlock()
31	measurement := b.getMeasurement(name, "Fastest Time", "Slowest Time", "Average Time", "s", 3, info...)
32	measurement.Results = append(measurement.Results, elapsedTime.Seconds())
33
34	return
35}
36
37func (b *benchmarker) RecordValue(name string, value float64, info ...interface{}) {
38	b.mu.Lock()
39	measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", "", 3, info...)
40	defer b.mu.Unlock()
41	measurement.Results = append(measurement.Results, value)
42}
43
44func (b *benchmarker) RecordValueWithPrecision(name string, value float64, units string, precision int, info ...interface{}) {
45	b.mu.Lock()
46	measurement := b.getMeasurement(name, "Smallest", " Largest", " Average", units, precision, info...)
47	defer b.mu.Unlock()
48	measurement.Results = append(measurement.Results, value)
49}
50
51func (b *benchmarker) getMeasurement(name string, smallestLabel string, largestLabel string, averageLabel string, units string, precision int, info ...interface{}) *types.SpecMeasurement {
52	measurement, ok := b.measurements[name]
53	if !ok {
54		var computedInfo interface{}
55		computedInfo = nil
56		if len(info) > 0 {
57			computedInfo = info[0]
58		}
59		measurement = &types.SpecMeasurement{
60			Name:          name,
61			Info:          computedInfo,
62			Order:         b.orderCounter,
63			SmallestLabel: smallestLabel,
64			LargestLabel:  largestLabel,
65			AverageLabel:  averageLabel,
66			Units:         units,
67			Precision:     precision,
68			Results:       make([]float64, 0),
69		}
70		b.measurements[name] = measurement
71		b.orderCounter++
72	}
73
74	return measurement
75}
76
77func (b *benchmarker) measurementsReport() map[string]*types.SpecMeasurement {
78	b.mu.Lock()
79	defer b.mu.Unlock()
80	for _, measurement := range b.measurements {
81		measurement.Smallest = math.MaxFloat64
82		measurement.Largest = -math.MaxFloat64
83		sum := float64(0)
84		sumOfSquares := float64(0)
85
86		for _, result := range measurement.Results {
87			if result > measurement.Largest {
88				measurement.Largest = result
89			}
90			if result < measurement.Smallest {
91				measurement.Smallest = result
92			}
93			sum += result
94			sumOfSquares += result * result
95		}
96
97		n := float64(len(measurement.Results))
98		measurement.Average = sum / n
99		measurement.StdDeviation = math.Sqrt(sumOfSquares/n - (sum/n)*(sum/n))
100	}
101
102	return b.measurements
103}
104