Skip to content

Commit c8b56bd

Browse files
dgryskideadprogram
authored andcommitted
src/testing: add b.ReportMetric()
1 parent fe20079 commit c8b56bd

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

src/testing/benchmark.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import (
1313
"math"
1414
"os"
1515
"runtime"
16+
"slices"
1617
"strconv"
1718
"strings"
1819
"time"
20+
"unicode"
1921
)
2022

2123
func initBenchmarkFlags() {
@@ -98,6 +100,7 @@ type B struct {
98100
// net total after running benchmar
99101
netAllocs uint64
100102
netBytes uint64
103+
extra map[string]float64
101104
}
102105

103106
// StartTimer starts timing a test. This function is called automatically
@@ -249,7 +252,7 @@ func (b *B) launch() {
249252
b.runN(int(n))
250253
}
251254
}
252-
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes}
255+
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.extra}
253256
}
254257

255258
// BenchmarkResult contains the results of a benchmark run.
@@ -260,10 +263,35 @@ type BenchmarkResult struct {
260263

261264
MemAllocs uint64 // The total number of memory allocations.
262265
MemBytes uint64 // The total number of bytes allocated.
266+
267+
// Extra records additional metrics reported by ReportMetric.
268+
Extra map[string]float64
269+
}
270+
271+
// ReportMetric adds "n unit" to the reported benchmark results.
272+
// If the metric is per-iteration, the caller should divide by b.N,
273+
// and by convention units should end in "/op".
274+
// ReportMetric overrides any previously reported value for the same unit.
275+
// ReportMetric panics if unit is the empty string or if unit contains
276+
// any whitespace.
277+
// If unit is a unit normally reported by the benchmark framework itself
278+
// (such as "allocs/op"), ReportMetric will override that metric.
279+
// Setting "ns/op" to 0 will suppress that built-in metric.
280+
func (b *B) ReportMetric(n float64, unit string) {
281+
if unit == "" {
282+
panic("metric unit must not be empty")
283+
}
284+
if strings.IndexFunc(unit, unicode.IsSpace) >= 0 {
285+
panic("metric unit must not contain whitespace")
286+
}
287+
b.extra[unit] = n
263288
}
264289

265290
// NsPerOp returns the "ns/op" metric.
266291
func (r BenchmarkResult) NsPerOp() int64 {
292+
if v, ok := r.Extra["ns/op"]; ok {
293+
return int64(v)
294+
}
267295
if r.N <= 0 {
268296
return 0
269297
}
@@ -272,6 +300,9 @@ func (r BenchmarkResult) NsPerOp() int64 {
272300

273301
// mbPerSec returns the "MB/s" metric.
274302
func (r BenchmarkResult) mbPerSec() float64 {
303+
if v, ok := r.Extra["MB/s"]; ok {
304+
return v
305+
}
275306
if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
276307
return 0
277308
}
@@ -281,6 +312,9 @@ func (r BenchmarkResult) mbPerSec() float64 {
281312
// AllocsPerOp returns the "allocs/op" metric,
282313
// which is calculated as r.MemAllocs / r.N.
283314
func (r BenchmarkResult) AllocsPerOp() int64 {
315+
if v, ok := r.Extra["allocs/op"]; ok {
316+
return int64(v)
317+
}
284318
if r.N <= 0 {
285319
return 0
286320
}
@@ -290,6 +324,9 @@ func (r BenchmarkResult) AllocsPerOp() int64 {
290324
// AllocedBytesPerOp returns the "B/op" metric,
291325
// which is calculated as r.MemBytes / r.N.
292326
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
327+
if v, ok := r.Extra["B/op"]; ok {
328+
return int64(v)
329+
}
293330
if r.N <= 0 {
294331
return 0
295332
}
@@ -308,7 +345,10 @@ func (r BenchmarkResult) String() string {
308345
fmt.Fprintf(buf, "%8d", r.N)
309346

310347
// Get ns/op as a float.
311-
ns := float64(r.T.Nanoseconds()) / float64(r.N)
348+
ns, ok := r.Extra["ns/op"]
349+
if !ok {
350+
ns = float64(r.T.Nanoseconds()) / float64(r.N)
351+
}
312352
if ns != 0 {
313353
buf.WriteByte('\t')
314354
prettyPrint(buf, ns, "ns/op")
@@ -317,6 +357,23 @@ func (r BenchmarkResult) String() string {
317357
if mbs := r.mbPerSec(); mbs != 0 {
318358
fmt.Fprintf(buf, "\t%7.2f MB/s", mbs)
319359
}
360+
361+
// Print extra metrics that aren't represented in the standard
362+
// metrics.
363+
var extraKeys []string
364+
for k := range r.Extra {
365+
switch k {
366+
case "ns/op", "MB/s", "B/op", "allocs/op":
367+
// Built-in metrics reported elsewhere.
368+
continue
369+
}
370+
extraKeys = append(extraKeys, k)
371+
}
372+
slices.Sort(extraKeys)
373+
for _, k := range extraKeys {
374+
buf.WriteByte('\t')
375+
prettyPrint(buf, r.Extra[k], k)
376+
}
320377
return buf.String()
321378
}
322379

0 commit comments

Comments
 (0)