aboutsummaryrefslogtreecommitdiff
path: root/benchmark/parse/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'benchmark/parse/parse.go')
-rw-r--r--benchmark/parse/parse.go131
1 files changed, 131 insertions, 0 deletions
diff --git a/benchmark/parse/parse.go b/benchmark/parse/parse.go
new file mode 100644
index 0000000..b37e6f0
--- /dev/null
+++ b/benchmark/parse/parse.go
@@ -0,0 +1,131 @@
+// 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 parse provides support for parsing benchmark results as
+// generated by 'go test -bench'.
+package parse // import "golang.org/x/tools/benchmark/parse"
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+// Flags used by Benchmark.Measured to indicate
+// which measurements a Benchmark contains.
+const (
+ NsPerOp = 1 << iota
+ MBPerS
+ AllocedBytesPerOp
+ AllocsPerOp
+)
+
+// Benchmark is one run of a single benchmark.
+type Benchmark struct {
+ Name string // benchmark name
+ N int // number of iterations
+ NsPerOp float64 // nanoseconds per iteration
+ AllocedBytesPerOp uint64 // bytes allocated per iteration
+ AllocsPerOp uint64 // allocs per iteration
+ MBPerS float64 // MB processed per second
+ Measured int // which measurements were recorded
+ Ord int // ordinal position within a benchmark run
+}
+
+// ParseLine extracts a Benchmark from a single line of testing.B
+// output.
+func ParseLine(line string) (*Benchmark, 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 := &Benchmark{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 *Benchmark) parseMeasurement(quant string, unit string) {
+ switch unit {
+ case "ns/op":
+ if f, err := strconv.ParseFloat(quant, 64); err == nil {
+ b.NsPerOp = f
+ b.Measured |= NsPerOp
+ }
+ case "MB/s":
+ if f, err := strconv.ParseFloat(quant, 64); err == nil {
+ b.MBPerS = f
+ b.Measured |= MBPerS
+ }
+ case "B/op":
+ if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
+ b.AllocedBytesPerOp = i
+ b.Measured |= AllocedBytesPerOp
+ }
+ case "allocs/op":
+ if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
+ b.AllocsPerOp = i
+ b.Measured |= AllocsPerOp
+ }
+ }
+}
+
+func (b *Benchmark) String() string {
+ buf := new(bytes.Buffer)
+ fmt.Fprintf(buf, "%s %d", b.Name, b.N)
+ if (b.Measured & NsPerOp) != 0 {
+ fmt.Fprintf(buf, " %.2f ns/op", b.NsPerOp)
+ }
+ if (b.Measured & MBPerS) != 0 {
+ fmt.Fprintf(buf, " %.2f MB/s", b.MBPerS)
+ }
+ if (b.Measured & AllocedBytesPerOp) != 0 {
+ fmt.Fprintf(buf, " %d B/op", b.AllocedBytesPerOp)
+ }
+ if (b.Measured & AllocsPerOp) != 0 {
+ fmt.Fprintf(buf, " %d allocs/op", b.AllocsPerOp)
+ }
+ return buf.String()
+}
+
+// Set is a collection of benchmarks from one
+// testing.B run, keyed by name to facilitate comparison.
+type Set map[string][]*Benchmark
+
+// ParseSet extracts a Set from testing.B output.
+// ParseSet preserves the order of benchmarks that have identical
+// names.
+func ParseSet(r io.Reader) (Set, error) {
+ bb := make(Set)
+ scan := bufio.NewScanner(r)
+ ord := 0
+ for scan.Scan() {
+ if b, err := ParseLine(scan.Text()); err == nil {
+ b.Ord = ord
+ ord++
+ bb[b.Name] = append(bb[b.Name], b)
+ }
+ }
+
+ if err := scan.Err(); err != nil {
+ return nil, err
+ }
+
+ return bb, nil
+}