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 "internal/asan"
13 "internal/msan"
14 "internal/race"
15 "internal/testenv"
16 "runtime"
17 "slices"
18 "strings"
19 "sync/atomic"
20 "testing"
21 "text/template"
22 "time"
23 )
24
25 var prettyPrintTests = []struct {
26 v float64
27 expected string
28 }{
29 {0, " 0 x"},
30 {1234.1, " 1234 x"},
31 {-1234.1, " -1234 x"},
32 {999.950001, " 1000 x"},
33 {999.949999, " 999.9 x"},
34 {99.9950001, " 100.0 x"},
35 {99.9949999, " 99.99 x"},
36 {-99.9949999, " -99.99 x"},
37 {0.000999950001, " 0.001000 x"},
38 {0.000999949999, " 0.0009999 x"},
39 {0.0000999949999, " 0.0001000 x"},
40 }
41
42 func TestPrettyPrint(t *testing.T) {
43 for _, tt := range prettyPrintTests {
44 buf := new(strings.Builder)
45 testing.PrettyPrint(buf, tt.v, "x")
46 if tt.expected != buf.String() {
47 t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
48 }
49 }
50 }
51
52 func TestResultString(t *testing.T) {
53
54 r := testing.BenchmarkResult{
55 N: 100,
56 T: 240 * time.Nanosecond,
57 }
58 if r.NsPerOp() != 2 {
59 t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
60 }
61 if want, got := " 100\t 2.400 ns/op", r.String(); want != got {
62 t.Errorf("String: expected %q, actual %q", want, got)
63 }
64
65
66 r.T = 40 * time.Nanosecond
67 if want, got := " 100\t 0.4000 ns/op", r.String(); want != got {
68 t.Errorf("String: expected %q, actual %q", want, got)
69 }
70
71
72 r.T = 0
73 if want, got := " 100", r.String(); want != got {
74 t.Errorf("String: expected %q, actual %q", want, got)
75 }
76 }
77
78 func TestRunParallel(t *testing.T) {
79 if testing.Short() {
80 t.Skip("skipping in short mode")
81 }
82 testing.Benchmark(func(b *testing.B) {
83 procs := uint32(0)
84 iters := uint64(0)
85 b.SetParallelism(3)
86 b.RunParallel(func(pb *testing.PB) {
87 atomic.AddUint32(&procs, 1)
88 for pb.Next() {
89 atomic.AddUint64(&iters, 1)
90 }
91 })
92 if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
93 t.Errorf("got %v procs, want %v", procs, want)
94 }
95 if iters != uint64(b.N) {
96 t.Errorf("got %v iters, want %v", iters, b.N)
97 }
98 })
99 }
100
101 func TestRunParallelFail(t *testing.T) {
102 testing.Benchmark(func(b *testing.B) {
103 b.RunParallel(func(pb *testing.PB) {
104
105
106 b.Log("log")
107 b.Error("error")
108 })
109 })
110 }
111
112 func TestRunParallelFatal(t *testing.T) {
113 testing.Benchmark(func(b *testing.B) {
114 b.RunParallel(func(pb *testing.PB) {
115 for pb.Next() {
116 if b.N > 1 {
117 b.Fatal("error")
118 }
119 }
120 })
121 })
122 }
123
124 func TestRunParallelSkipNow(t *testing.T) {
125 testing.Benchmark(func(b *testing.B) {
126 b.RunParallel(func(pb *testing.PB) {
127 for pb.Next() {
128 if b.N > 1 {
129 b.SkipNow()
130 }
131 }
132 })
133 })
134 }
135
136 func TestBenchmarkContext(t *testing.T) {
137 testing.Benchmark(func(b *testing.B) {
138 ctx := b.Context()
139 if err := ctx.Err(); err != nil {
140 b.Fatalf("expected non-canceled context, got %v", err)
141 }
142
143 var innerCtx context.Context
144 b.Run("inner", func(b *testing.B) {
145 innerCtx = b.Context()
146 if err := innerCtx.Err(); err != nil {
147 b.Fatalf("expected inner benchmark to not inherit canceled context, got %v", err)
148 }
149 })
150 b.Run("inner2", func(b *testing.B) {
151 if !errors.Is(innerCtx.Err(), context.Canceled) {
152 t.Fatal("expected context of sibling benchmark to be canceled after its test function finished")
153 }
154 })
155
156 t.Cleanup(func() {
157 if !errors.Is(ctx.Err(), context.Canceled) {
158 t.Fatal("expected context canceled before cleanup")
159 }
160 })
161 })
162 }
163
164
165
166
167
168 func newX() []byte {
169 out := make([]byte, 8)
170 return use1(out)
171 }
172
173
174 func use1(out []byte) []byte {
175 return out
176 }
177
178
179
180
181
182 func use2(x any) {}
183
184 func TestBenchmarkBLoopAllocs(t *testing.T) {
185 testenv.SkipIfOptimizationOff(t)
186 if race.Enabled || asan.Enabled || msan.Enabled {
187 t.Skip("skipping in case sanitizers alter allocation behavior")
188 }
189
190 t.Run("call-result", func(t *testing.T) {
191 bRet := testing.Benchmark(func(b *testing.B) {
192 b.ReportAllocs()
193 for b.Loop() {
194 newX()
195 }
196 })
197 if bRet.N == 0 {
198 t.Fatalf("benchmark reported 0 iterations")
199 }
200 if bRet.AllocsPerOp() != 0 {
201 t.Errorf("want 0 allocs, got %d", bRet.AllocsPerOp())
202 }
203 })
204
205 t.Run("call-arg", func(t *testing.T) {
206 bRet := testing.Benchmark(func(b *testing.B) {
207 b.ReportAllocs()
208 for b.Loop() {
209 use2(make([]byte, 1000))
210 }
211 })
212 if bRet.N == 0 {
213 t.Fatalf("benchmark reported 0 iterations")
214 }
215 if bRet.AllocsPerOp() != 0 {
216 t.Errorf("want 0 allocs, got %d", bRet.AllocsPerOp())
217 }
218 })
219 }
220
221 func ExampleB_RunParallel() {
222
223 testing.Benchmark(func(b *testing.B) {
224 templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
225
226
227 b.RunParallel(func(pb *testing.PB) {
228
229 var buf bytes.Buffer
230 for pb.Next() {
231
232 buf.Reset()
233 templ.Execute(&buf, "World")
234 }
235 })
236 })
237 }
238
239 func TestReportMetric(t *testing.T) {
240 res := testing.Benchmark(func(b *testing.B) {
241 b.ReportMetric(12345, "ns/op")
242 b.ReportMetric(0.2, "frobs/op")
243 })
244
245 if res.NsPerOp() != 12345 {
246 t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
247 }
248
249 res.N = 1
250 want := " 1\t 12345 ns/op\t 0.2000 frobs/op"
251 if want != res.String() {
252 t.Errorf("expected %q, actual %q", want, res.String())
253 }
254 }
255
256 func ExampleB_ReportMetric() {
257
258
259 testing.Benchmark(func(b *testing.B) {
260 var compares int64
261 for b.Loop() {
262 s := []int{5, 4, 3, 2, 1}
263 slices.SortFunc(s, func(a, b int) int {
264 compares++
265 return cmp.Compare(a, b)
266 })
267 }
268
269
270 b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
271
272
273 b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns")
274 })
275 }
276
277 func ExampleB_ReportMetric_parallel() {
278
279
280 testing.Benchmark(func(b *testing.B) {
281 var compares atomic.Int64
282 b.RunParallel(func(pb *testing.PB) {
283 for pb.Next() {
284 s := []int{5, 4, 3, 2, 1}
285 slices.SortFunc(s, func(a, b int) int {
286
287
288
289 compares.Add(1)
290 return cmp.Compare(a, b)
291 })
292 }
293 })
294
295
296
297
298
299
300 b.ReportMetric(float64(compares.Load())/float64(b.N), "compares/op")
301
302
303 b.ReportMetric(float64(compares.Load())/float64(b.Elapsed().Nanoseconds()), "compares/ns")
304 })
305 }
306
View as plain text