123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- // 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
- }
|