1 | // Copyright 2014 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Package parse provides support for parsing benchmark results as |
6 | // generated by 'go test -bench'. |
7 | package parse // import "golang.org/x/tools/benchmark/parse" |
8 | |
9 | import ( |
10 | "bufio" |
11 | "bytes" |
12 | "fmt" |
13 | "io" |
14 | "strconv" |
15 | "strings" |
16 | ) |
17 | |
18 | // Flags used by Benchmark.Measured to indicate |
19 | // which measurements a Benchmark contains. |
20 | const ( |
21 | NsPerOp = 1 << iota |
22 | MBPerS |
23 | AllocedBytesPerOp |
24 | AllocsPerOp |
25 | ) |
26 | |
27 | // Benchmark is one run of a single benchmark. |
28 | type Benchmark struct { |
29 | Name string // benchmark name |
30 | N int // number of iterations |
31 | NsPerOp float64 // nanoseconds per iteration |
32 | AllocedBytesPerOp uint64 // bytes allocated per iteration |
33 | AllocsPerOp uint64 // allocs per iteration |
34 | MBPerS float64 // MB processed per second |
35 | Measured int // which measurements were recorded |
36 | Ord int // ordinal position within a benchmark run |
37 | } |
38 | |
39 | // ParseLine extracts a Benchmark from a single line of testing.B |
40 | // output. |
41 | func ParseLine(line string) (*Benchmark, error) { |
42 | fields := strings.Fields(line) |
43 | |
44 | // Two required, positional fields: Name and iterations. |
45 | if len(fields) < 2 { |
46 | return nil, fmt.Errorf("two fields required, have %d", len(fields)) |
47 | } |
48 | if !strings.HasPrefix(fields[0], "Benchmark") { |
49 | return nil, fmt.Errorf(`first field does not start with "Benchmark"`) |
50 | } |
51 | n, err := strconv.Atoi(fields[1]) |
52 | if err != nil { |
53 | return nil, err |
54 | } |
55 | b := &Benchmark{Name: fields[0], N: n} |
56 | |
57 | // Parse any remaining pairs of fields; we've parsed one pair already. |
58 | for i := 1; i < len(fields)/2; i++ { |
59 | b.parseMeasurement(fields[i*2], fields[i*2+1]) |
60 | } |
61 | return b, nil |
62 | } |
63 | |
64 | func (b *Benchmark) parseMeasurement(quant string, unit string) { |
65 | switch unit { |
66 | case "ns/op": |
67 | if f, err := strconv.ParseFloat(quant, 64); err == nil { |
68 | b.NsPerOp = f |
69 | b.Measured |= NsPerOp |
70 | } |
71 | case "MB/s": |
72 | if f, err := strconv.ParseFloat(quant, 64); err == nil { |
73 | b.MBPerS = f |
74 | b.Measured |= MBPerS |
75 | } |
76 | case "B/op": |
77 | if i, err := strconv.ParseUint(quant, 10, 64); err == nil { |
78 | b.AllocedBytesPerOp = i |
79 | b.Measured |= AllocedBytesPerOp |
80 | } |
81 | case "allocs/op": |
82 | if i, err := strconv.ParseUint(quant, 10, 64); err == nil { |
83 | b.AllocsPerOp = i |
84 | b.Measured |= AllocsPerOp |
85 | } |
86 | } |
87 | } |
88 | |
89 | func (b *Benchmark) String() string { |
90 | buf := new(bytes.Buffer) |
91 | fmt.Fprintf(buf, "%s %d", b.Name, b.N) |
92 | if (b.Measured & NsPerOp) != 0 { |
93 | fmt.Fprintf(buf, " %.2f ns/op", b.NsPerOp) |
94 | } |
95 | if (b.Measured & MBPerS) != 0 { |
96 | fmt.Fprintf(buf, " %.2f MB/s", b.MBPerS) |
97 | } |
98 | if (b.Measured & AllocedBytesPerOp) != 0 { |
99 | fmt.Fprintf(buf, " %d B/op", b.AllocedBytesPerOp) |
100 | } |
101 | if (b.Measured & AllocsPerOp) != 0 { |
102 | fmt.Fprintf(buf, " %d allocs/op", b.AllocsPerOp) |
103 | } |
104 | return buf.String() |
105 | } |
106 | |
107 | // Set is a collection of benchmarks from one |
108 | // testing.B run, keyed by name to facilitate comparison. |
109 | type Set map[string][]*Benchmark |
110 | |
111 | // ParseSet extracts a Set from testing.B output. |
112 | // ParseSet preserves the order of benchmarks that have identical |
113 | // names. |
114 | func ParseSet(r io.Reader) (Set, error) { |
115 | bb := make(Set) |
116 | scan := bufio.NewScanner(r) |
117 | ord := 0 |
118 | for scan.Scan() { |
119 | if b, err := ParseLine(scan.Text()); err == nil { |
120 | b.Ord = ord |
121 | ord++ |
122 | bb[b.Name] = append(bb[b.Name], b) |
123 | } |
124 | } |
125 | |
126 | if err := scan.Err(); err != nil { |
127 | return nil, err |
128 | } |
129 | |
130 | return bb, nil |
131 | } |
132 |
Members