github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/benchcmp/parse.go (about) 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 main 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "strconv" 13 "strings" 14 ) 15 16 // Flags used by Bench.Measured to indicate 17 // which measurements a Bench contains. 18 const ( 19 NsOp = 1 << iota 20 MbS 21 BOp 22 AllocsOp 23 ) 24 25 // Bench is one run of a single benchmark. 26 type Bench struct { 27 Name string // benchmark name 28 N int // number of iterations 29 NsOp float64 // nanoseconds per iteration 30 MbS float64 // MB processed per second 31 BOp uint64 // bytes allocated per iteration 32 AllocsOp uint64 // allocs per iteration 33 Measured int // which measurements were recorded 34 ord int // ordinal position within a benchmark run, used for sorting 35 } 36 37 // ParseLine extracts a Bench from a single line of testing.B output. 38 func ParseLine(line string) (*Bench, error) { 39 fields := strings.Fields(line) 40 41 // Two required, positional fields: Name and iterations. 42 if len(fields) < 2 { 43 return nil, fmt.Errorf("two fields required, have %d", len(fields)) 44 } 45 if !strings.HasPrefix(fields[0], "Benchmark") { 46 return nil, fmt.Errorf(`first field does not start with "Benchmark`) 47 } 48 n, err := strconv.Atoi(fields[1]) 49 if err != nil { 50 return nil, err 51 } 52 b := &Bench{Name: fields[0], N: n} 53 54 // Parse any remaining pairs of fields; we've parsed one pair already. 55 for i := 1; i < len(fields)/2; i++ { 56 b.parseMeasurement(fields[i*2], fields[i*2+1]) 57 } 58 return b, nil 59 } 60 61 func (b *Bench) parseMeasurement(quant string, unit string) { 62 switch unit { 63 case "ns/op": 64 if f, err := strconv.ParseFloat(quant, 64); err == nil { 65 b.NsOp = f 66 b.Measured |= NsOp 67 } 68 case "MB/s": 69 if f, err := strconv.ParseFloat(quant, 64); err == nil { 70 b.MbS = f 71 b.Measured |= MbS 72 } 73 case "B/op": 74 if i, err := strconv.ParseUint(quant, 10, 64); err == nil { 75 b.BOp = i 76 b.Measured |= BOp 77 } 78 case "allocs/op": 79 if i, err := strconv.ParseUint(quant, 10, 64); err == nil { 80 b.AllocsOp = i 81 b.Measured |= AllocsOp 82 } 83 } 84 } 85 86 func (b *Bench) String() string { 87 buf := new(bytes.Buffer) 88 fmt.Fprintf(buf, "%s %d", b.Name, b.N) 89 if b.Measured&NsOp != 0 { 90 fmt.Fprintf(buf, " %.2f ns/op", b.NsOp) 91 } 92 if b.Measured&MbS != 0 { 93 fmt.Fprintf(buf, " %.2f MB/s", b.MbS) 94 } 95 if b.Measured&BOp != 0 { 96 fmt.Fprintf(buf, " %d B/op", b.BOp) 97 } 98 if b.Measured&AllocsOp != 0 { 99 fmt.Fprintf(buf, " %d allocs/op", b.AllocsOp) 100 } 101 return buf.String() 102 } 103 104 // BenchSet is a collection of benchmarks from one 105 // testing.B run, keyed by name to facilitate comparison. 106 type BenchSet map[string][]*Bench 107 108 // Parse extracts a BenchSet from testing.B output. Parse 109 // preserves the order of benchmarks that have identical names. 110 func ParseBenchSet(r io.Reader) (BenchSet, error) { 111 bb := make(BenchSet) 112 scan := bufio.NewScanner(r) 113 ord := 0 114 for scan.Scan() { 115 if b, err := ParseLine(scan.Text()); err == nil { 116 b.ord = ord 117 ord++ 118 old := bb[b.Name] 119 if *best && old != nil { 120 if old[0].NsOp < b.NsOp { 121 continue 122 } 123 b.ord = old[0].ord 124 bb[b.Name] = old[:0] 125 } 126 bb[b.Name] = append(bb[b.Name], b) 127 } 128 } 129 130 if err := scan.Err(); err != nil { 131 return nil, err 132 } 133 134 return bb, nil 135 }