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  }