...
1
2
3
4
5
6
7 package benchmark
8
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "path/filepath"
14 "reflect"
15 "runtime"
16 "strings"
17 "time"
18 )
19
20 type CaseDefinition struct {
21 Bench BenchCase
22 Count int
23 Size int
24 RequiredIterations int
25 Runtime time.Duration
26
27 cumulativeRuntime time.Duration
28 elapsed time.Duration
29 startAt time.Time
30 isRunning bool
31 }
32
33
34
35 type TimerManager interface {
36 ResetTimer()
37 StartTimer()
38 StopTimer()
39 }
40
41 func (c *CaseDefinition) ResetTimer() {
42 c.startAt = time.Now()
43 c.elapsed = 0
44 c.isRunning = true
45 }
46
47 func (c *CaseDefinition) StartTimer() {
48 c.startAt = time.Now()
49 c.isRunning = true
50 }
51
52 func (c *CaseDefinition) StopTimer() {
53 if !c.isRunning {
54 return
55 }
56 c.elapsed += time.Since(c.startAt)
57 c.isRunning = false
58 }
59
60 func (c *CaseDefinition) Run(ctx context.Context) *BenchResult {
61 out := &BenchResult{
62 Trials: 1,
63 DataSize: c.Size,
64 Name: c.Name(),
65 Operations: c.Count,
66 }
67 var cancel context.CancelFunc
68 ctx, cancel = context.WithTimeout(ctx, 2*ExecutionTimeout)
69 defer cancel()
70
71 fmt.Println("=== RUN", out.Name)
72 if c.RequiredIterations == 0 {
73 c.RequiredIterations = MinIterations
74 }
75
76 benchRepeat:
77 for {
78 if ctx.Err() != nil {
79 break
80 }
81 if c.cumulativeRuntime >= c.Runtime {
82 if out.Trials >= c.RequiredIterations {
83 break
84 } else if c.cumulativeRuntime >= ExecutionTimeout {
85 break
86 }
87 }
88
89 res := Result{
90 Iterations: c.Count,
91 }
92
93 c.StartTimer()
94 res.Error = c.Bench(ctx, c, c.Count)
95 c.StopTimer()
96 res.Duration = c.elapsed
97 c.cumulativeRuntime += res.Duration
98
99 switch {
100 case errors.Is(res.Error, context.DeadlineExceeded):
101 break benchRepeat
102 case errors.Is(res.Error, context.Canceled):
103 break benchRepeat
104 case res.Error == nil:
105 out.Trials++
106 c.elapsed = 0
107 out.Raw = append(out.Raw, res)
108 default:
109 continue
110 }
111
112 }
113
114 out.Duration = out.totalDuration()
115 fmt.Printf(" --- REPORT: count=%d trials=%d requiredTrials=%d runtime=%s\n",
116 c.Count, out.Trials, c.RequiredIterations, c.Runtime)
117 if out.HasErrors() {
118 fmt.Printf(" --- ERRORS: %s\n", strings.Join(out.errReport(), "\n "))
119 fmt.Printf("--- FAIL: %s (%s)\n", out.Name, out.roundedRuntime())
120 } else {
121 fmt.Printf("--- PASS: %s (%s)\n", out.Name, out.roundedRuntime())
122 }
123
124 return out
125
126 }
127
128 func (c *CaseDefinition) String() string {
129 return fmt.Sprintf("name=%s, count=%d, runtime=%s timeout=%s",
130 c.Name(), c.Count, c.Runtime, ExecutionTimeout)
131 }
132
133 func (c *CaseDefinition) Name() string { return getName(c.Bench) }
134 func getName(i interface{}) string {
135 n := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
136 parts := strings.Split(n, ".")
137 if len(parts) > 1 {
138 return parts[len(parts)-1]
139 }
140
141 return n
142
143 }
144
145 func getProjectRoot() string { return filepath.Dir(getDirectoryOfFile()) }
146
147 func getDirectoryOfFile() string {
148 _, file, _, _ := runtime.Caller(1)
149
150 return filepath.Dir(file)
151 }
152
View as plain text