mirror of
https://github.com/golang/go
synced 2024-11-18 15:34:53 -07:00
222283a9c8
If you have multiple runs in old.txt and new.txt the default behavior is to match them up pairwise and compare successive pairs (and if you have a different number of runs in each file, benchcmp refuses to do anything). The new -best flag changes the behavior to instead compare the fastest run of each benchmark from the two files. This makes sense if you believe that the fastest speed is the 'actual' speed and the slower results are due to the computer spending time doing non-benchmark work while the benchmark was running. LGTM=josharian R=golang-codereviews, josharian CC=golang-codereviews https://golang.org/cl/102890047
136 lines
3.2 KiB
Go
136 lines
3.2 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Flags used by Bench.Measured to indicate
|
|
// which measurements a Bench contains.
|
|
const (
|
|
NsOp = 1 << iota
|
|
MbS
|
|
BOp
|
|
AllocsOp
|
|
)
|
|
|
|
// Bench is one run of a single benchmark.
|
|
type Bench struct {
|
|
Name string // benchmark name
|
|
N int // number of iterations
|
|
NsOp float64 // nanoseconds per iteration
|
|
MbS float64 // MB processed per second
|
|
BOp uint64 // bytes allocated per iteration
|
|
AllocsOp uint64 // allocs per iteration
|
|
Measured int // which measurements were recorded
|
|
ord int // ordinal position within a benchmark run, used for sorting
|
|
}
|
|
|
|
// ParseLine extracts a Bench from a single line of testing.B output.
|
|
func ParseLine(line string) (*Bench, error) {
|
|
fields := strings.Fields(line)
|
|
|
|
// Two required, positional fields: Name and iterations.
|
|
if len(fields) < 2 {
|
|
return nil, fmt.Errorf("two fields required, have %d", len(fields))
|
|
}
|
|
if !strings.HasPrefix(fields[0], "Benchmark") {
|
|
return nil, fmt.Errorf(`first field does not start with "Benchmark`)
|
|
}
|
|
n, err := strconv.Atoi(fields[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b := &Bench{Name: fields[0], N: n}
|
|
|
|
// Parse any remaining pairs of fields; we've parsed one pair already.
|
|
for i := 1; i < len(fields)/2; i++ {
|
|
b.parseMeasurement(fields[i*2], fields[i*2+1])
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
func (b *Bench) parseMeasurement(quant string, unit string) {
|
|
switch unit {
|
|
case "ns/op":
|
|
if f, err := strconv.ParseFloat(quant, 64); err == nil {
|
|
b.NsOp = f
|
|
b.Measured |= NsOp
|
|
}
|
|
case "MB/s":
|
|
if f, err := strconv.ParseFloat(quant, 64); err == nil {
|
|
b.MbS = f
|
|
b.Measured |= MbS
|
|
}
|
|
case "B/op":
|
|
if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
|
|
b.BOp = i
|
|
b.Measured |= BOp
|
|
}
|
|
case "allocs/op":
|
|
if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
|
|
b.AllocsOp = i
|
|
b.Measured |= AllocsOp
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Bench) String() string {
|
|
buf := new(bytes.Buffer)
|
|
fmt.Fprintf(buf, "%s %d", b.Name, b.N)
|
|
if b.Measured&NsOp != 0 {
|
|
fmt.Fprintf(buf, " %.2f ns/op", b.NsOp)
|
|
}
|
|
if b.Measured&MbS != 0 {
|
|
fmt.Fprintf(buf, " %.2f MB/s", b.MbS)
|
|
}
|
|
if b.Measured&BOp != 0 {
|
|
fmt.Fprintf(buf, " %d B/op", b.BOp)
|
|
}
|
|
if b.Measured&AllocsOp != 0 {
|
|
fmt.Fprintf(buf, " %d allocs/op", b.AllocsOp)
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
// BenchSet is a collection of benchmarks from one
|
|
// testing.B run, keyed by name to facilitate comparison.
|
|
type BenchSet map[string][]*Bench
|
|
|
|
// Parse extracts a BenchSet from testing.B output. Parse
|
|
// preserves the order of benchmarks that have identical names.
|
|
func ParseBenchSet(r io.Reader) (BenchSet, error) {
|
|
bb := make(BenchSet)
|
|
scan := bufio.NewScanner(r)
|
|
ord := 0
|
|
for scan.Scan() {
|
|
if b, err := ParseLine(scan.Text()); err == nil {
|
|
b.ord = ord
|
|
ord++
|
|
old := bb[b.Name]
|
|
if *best && old != nil {
|
|
if old[0].NsOp < b.NsOp {
|
|
continue
|
|
}
|
|
b.ord = old[0].ord
|
|
bb[b.Name] = old[:0]
|
|
}
|
|
bb[b.Name] = append(bb[b.Name], b)
|
|
}
|
|
}
|
|
|
|
if err := scan.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return bb, nil
|
|
}
|