github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/benchcmp/benchcmp.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 "flag" 9 "fmt" 10 "os" 11 "sort" 12 "strconv" 13 "text/tabwriter" 14 15 "golang.org/x/tools/benchmark/parse" 16 ) 17 18 var ( 19 changedOnly = flag.Bool("changed", false, "show only benchmarks that have changed") 20 magSort = flag.Bool("mag", false, "sort benchmarks by magnitude of change") 21 best = flag.Bool("best", false, "compare best times from old and new") 22 ) 23 24 const usageFooter = ` 25 Each input file should be from: 26 go test -run=NONE -bench=. > [old,new].txt 27 28 Benchcmp compares old and new for each benchmark. 29 30 If -test.benchmem=true is added to the "go test" command 31 benchcmp will also compare memory allocations. 32 ` 33 34 func main() { 35 flag.Usage = func() { 36 fmt.Fprintf(os.Stderr, "usage: %s old.txt new.txt\n\n", os.Args[0]) 37 flag.PrintDefaults() 38 fmt.Fprint(os.Stderr, usageFooter) 39 os.Exit(2) 40 } 41 flag.Parse() 42 if flag.NArg() != 2 { 43 flag.Usage() 44 } 45 46 before := parseFile(flag.Arg(0)) 47 after := parseFile(flag.Arg(1)) 48 49 cmps, warnings := Correlate(before, after) 50 51 for _, warn := range warnings { 52 fmt.Fprintln(os.Stderr, warn) 53 } 54 55 if len(cmps) == 0 { 56 fatal("benchcmp: no repeated benchmarks") 57 } 58 59 w := new(tabwriter.Writer) 60 w.Init(os.Stdout, 0, 0, 5, ' ', 0) 61 defer w.Flush() 62 63 var header bool // Has the header has been displayed yet for a given block? 64 65 if *magSort { 66 sort.Sort(ByDeltaNsPerOp(cmps)) 67 } else { 68 sort.Sort(ByParseOrder(cmps)) 69 } 70 for _, cmp := range cmps { 71 if !cmp.Measured(parse.NsPerOp) { 72 continue 73 } 74 if delta := cmp.DeltaNsPerOp(); !*changedOnly || delta.Changed() { 75 if !header { 76 fmt.Fprint(w, "benchmark\told ns/op\tnew ns/op\tdelta\n") 77 header = true 78 } 79 fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", cmp.Name(), formatNs(cmp.Before.NsPerOp), formatNs(cmp.After.NsPerOp), delta.Percent()) 80 } 81 } 82 83 header = false 84 if *magSort { 85 sort.Sort(ByDeltaMBPerS(cmps)) 86 } 87 for _, cmp := range cmps { 88 if !cmp.Measured(parse.MBPerS) { 89 continue 90 } 91 if delta := cmp.DeltaMBPerS(); !*changedOnly || delta.Changed() { 92 if !header { 93 fmt.Fprint(w, "\nbenchmark\told MB/s\tnew MB/s\tspeedup\n") 94 header = true 95 } 96 fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\n", cmp.Name(), cmp.Before.MBPerS, cmp.After.MBPerS, delta.Multiple()) 97 } 98 } 99 100 header = false 101 if *magSort { 102 sort.Sort(ByDeltaAllocsPerOp(cmps)) 103 } 104 for _, cmp := range cmps { 105 if !cmp.Measured(parse.AllocsPerOp) { 106 continue 107 } 108 if delta := cmp.DeltaAllocsPerOp(); !*changedOnly || delta.Changed() { 109 if !header { 110 fmt.Fprint(w, "\nbenchmark\told allocs\tnew allocs\tdelta\n") 111 header = true 112 } 113 fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocsPerOp, cmp.After.AllocsPerOp, delta.Percent()) 114 } 115 } 116 117 header = false 118 if *magSort { 119 sort.Sort(ByDeltaAllocedBytesPerOp(cmps)) 120 } 121 for _, cmp := range cmps { 122 if !cmp.Measured(parse.AllocedBytesPerOp) { 123 continue 124 } 125 if delta := cmp.DeltaAllocedBytesPerOp(); !*changedOnly || delta.Changed() { 126 if !header { 127 fmt.Fprint(w, "\nbenchmark\told bytes\tnew bytes\tdelta\n") 128 header = true 129 } 130 fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocedBytesPerOp, cmp.After.AllocedBytesPerOp, cmp.DeltaAllocedBytesPerOp().Percent()) 131 } 132 } 133 } 134 135 func fatal(msg interface{}) { 136 fmt.Fprintln(os.Stderr, msg) 137 os.Exit(1) 138 } 139 140 func parseFile(path string) parse.Set { 141 f, err := os.Open(path) 142 if err != nil { 143 fatal(err) 144 } 145 defer f.Close() 146 bb, err := parse.ParseSet(f) 147 if err != nil { 148 fatal(err) 149 } 150 if *best { 151 selectBest(bb) 152 } 153 return bb 154 } 155 156 func selectBest(bs parse.Set) { 157 for name, bb := range bs { 158 if len(bb) < 2 { 159 continue 160 } 161 ord := bb[0].Ord 162 best := bb[0] 163 for _, b := range bb { 164 if b.NsPerOp < best.NsPerOp { 165 b.Ord = ord 166 best = b 167 } 168 } 169 bs[name] = []*parse.Benchmark{best} 170 } 171 } 172 173 // formatNs formats ns measurements to expose a useful amount of 174 // precision. It mirrors the ns precision logic of testing.B. 175 func formatNs(ns float64) string { 176 prec := 0 177 switch { 178 case ns < 10: 179 prec = 2 180 case ns < 100: 181 prec = 1 182 } 183 return strconv.FormatFloat(ns, 'f', prec, 64) 184 }