Source file
src/testing/benchmark_test.go
1
2
3
4
5 package testing_test
6
7 import (
8 "bytes"
9 "cmp"
10 "context"
11 "errors"
12 "runtime"
13 "slices"
14 "strings"
15 "sync/atomic"
16 "testing"
17 "text/template"
18 "time"
19 )
20
21 var prettyPrintTests = []struct {
22 v float64
23 expected string
24 }{
25 {0, " 0 x"},
26 {1234.1, " 1234 x"},
27 {-1234.1, " -1234 x"},
28 {999.950001, " 1000 x"},
29 {999.949999, " 999.9 x"},
30 {99.9950001, " 100.0 x"},
31 {99.9949999, " 99.99 x"},
32 {-99.9949999, " -99.99 x"},
33 {0.000999950001, " 0.001000 x"},
34 {0.000999949999, " 0.0009999 x"},
35 {0.0000999949999, " 0.0001000 x"},
36 }
37
38 func TestPrettyPrint(t *testing.T) {
39 for _, tt := range prettyPrintTests {
40 buf := new(strings.Builder)
41 testing.PrettyPrint(buf, tt.v, "x")
42 if tt.expected != buf.String() {
43 t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
44 }
45 }
46 }
47
48 func TestResultString(t *testing.T) {
49
50 r := testing.BenchmarkResult{
51 N: 100,
52 T: 240 * time.Nanosecond,
53 }
54 if r.NsPerOp() != 2 {
55 t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
56 }
57 if want, got := " 100\t 2.400 ns/op", r.String(); want != got {
58 t.Errorf("String: expected %q, actual %q", want, got)
59 }
60
61
62 r.T = 40 * time.Nanosecond
63 if want, got := " 100\t 0.4000 ns/op", r.String(); want != got {
64 t.Errorf("String: expected %q, actual %q", want, got)
65 }
66
67
68 r.T = 0
69 if want, got := " 100", r.String(); want != got {
70 t.Errorf("String: expected %q, actual %q", want, got)
71 }
72 }
73
74 func TestRunParallel(t *testing.T) {
75 if testing.Short() {
76 t.Skip("skipping in short mode")
77 }
78 testing.Benchmark(func(b *testing.B) {
79 procs := uint32(0)
80 iters := uint64(0)
81 b.SetParallelism(3)
82 b.RunParallel(func(pb *testing.PB) {
83 atomic.AddUint32(&procs, 1)
84 for pb.Next() {
85 atomic.AddUint64(&iters, 1)
86 }
87 })
88 if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
89 t.Errorf("got %v procs, want %v", procs, want)
90 }
91 if iters != uint64(b.N) {
92 t.Errorf("got %v iters, want %v", iters, b.N)
93 }
94 })
95 }
96
97 func TestRunParallelFail(t *testing.T) {
98 testing.Benchmark(func(b *testing.B) {
99 b.RunParallel(func(pb *testing.PB) {
100
101
102 b.Log("log")
103 b.Error("error")
104 })
105 })
106 }
107
108 func TestRunParallelFatal(t *testing.T) {
109 testing.Benchmark(func(b *testing.B) {
110 b.RunParallel(func(pb *testing.PB) {
111 for pb.Next() {
112 if b.N > 1 {
113 b.Fatal("error")
114 }
115 }
116 })
117 })
118 }
119
120 func TestRunParallelSkipNow(t *testing.T) {
121 testing.Benchmark(func(b *testing.B) {
122 b.RunParallel(func(pb *testing.PB) {
123 for pb.Next() {
124 if b.N > 1 {
125 b.SkipNow()
126 }
127 }
128 })
129 })
130 }
131
132 func TestBenchmarkContext(t *testing.T) {
133 testing.Benchmark(func(b *testing.B) {
134 ctx := b.Context()
135 if err := ctx.Err(); err != nil {
136 b.Fatalf("expected non-canceled context, got %v", err)
137 }
138
139 var innerCtx context.Context
140 b.Run("inner", func(b *testing.B) {
141 innerCtx = b.Context()
142 if err := innerCtx.Err(); err != nil {
143 b.Fatalf("expected inner benchmark to not inherit canceled context, got %v", err)
144 }
145 })
146 b.Run("inner2", func(b *testing.B) {
147 if !errors.Is(innerCtx.Err(), context.Canceled) {
148 t.Fatal("expected context of sibling benchmark to be canceled after its test function finished")
149 }
150 })
151
152 t.Cleanup(func() {
153 if !errors.Is(ctx.Err(), context.Canceled) {
154 t.Fatal("expected context canceled before cleanup")
155 }
156 })
157 })
158 }
159
160 func ExampleB_RunParallel() {
161
162 testing.Benchmark(func(b *testing.B) {
163 templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
164
165
166 b.RunParallel(func(pb *testing.PB) {
167
168 var buf bytes.Buffer
169 for pb.Next() {
170
171 buf.Reset()
172 templ.Execute(&buf, "World")
173 }
174 })
175 })
176 }
177
178 func TestReportMetric(t *testing.T) {
179 res := testing.Benchmark(func(b *testing.B) {
180 b.ReportMetric(12345, "ns/op")
181 b.ReportMetric(0.2, "frobs/op")
182 })
183
184 if res.NsPerOp() != 12345 {
185 t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
186 }
187
188 res.N = 1
189 want := " 1\t 12345 ns/op\t 0.2000 frobs/op"
190 if want != res.String() {
191 t.Errorf("expected %q, actual %q", want, res.String())
192 }
193 }
194
195 func ExampleB_ReportMetric() {
196
197
198 testing.Benchmark(func(b *testing.B) {
199 var compares int64
200 for b.Loop() {
201 s := []int{5, 4, 3, 2, 1}
202 slices.SortFunc(s, func(a, b int) int {
203 compares++
204 return cmp.Compare(a, b)
205 })
206 }
207
208
209 b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
210
211
212 b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns")
213 })
214 }
215
216 func ExampleB_ReportMetric_parallel() {
217
218
219 testing.Benchmark(func(b *testing.B) {
220 var compares atomic.Int64
221 b.RunParallel(func(pb *testing.PB) {
222 for pb.Next() {
223 s := []int{5, 4, 3, 2, 1}
224 slices.SortFunc(s, func(a, b int) int {
225
226
227
228 compares.Add(1)
229 return cmp.Compare(a, b)
230 })
231 }
232 })
233
234
235
236
237
238
239 b.ReportMetric(float64(compares.Load())/float64(b.N), "compares/op")
240
241
242 b.ReportMetric(float64(compares.Load())/float64(b.Elapsed().Nanoseconds()), "compares/ns")
243 })
244 }
245
View as plain text