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  }