@@ -13,9 +13,11 @@ import (
1313 "math"
1414 "os"
1515 "runtime"
16+ "slices"
1617 "strconv"
1718 "strings"
1819 "time"
20+ "unicode"
1921)
2022
2123func 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.
266291func (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.
274302func (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.
283314func (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.
292326func (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