File: parse.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.5.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-backports
  • size: 16,592 kB
  • sloc: javascript: 2,011; asm: 1,635; sh: 192; yacc: 155; makefile: 52; ansic: 8
file content (131 lines) | stat: -rw-r--r-- 3,467 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package parse provides support for parsing benchmark results as
// generated by 'go test -bench'.
package parse // import "golang.org/x/tools/benchmark/parse"

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strconv"
	"strings"
)

// Flags used by Benchmark.Measured to indicate
// which measurements a Benchmark contains.
const (
	NsPerOp = 1 << iota
	MBPerS
	AllocedBytesPerOp
	AllocsPerOp
)

// Benchmark is one run of a single benchmark.
type Benchmark struct {
	Name              string  // benchmark name
	N                 int     // number of iterations
	NsPerOp           float64 // nanoseconds per iteration
	AllocedBytesPerOp uint64  // bytes allocated per iteration
	AllocsPerOp       uint64  // allocs per iteration
	MBPerS            float64 // MB processed per second
	Measured          int     // which measurements were recorded
	Ord               int     // ordinal position within a benchmark run
}

// ParseLine extracts a Benchmark from a single line of testing.B
// output.
func ParseLine(line string) (*Benchmark, error) {
	fields := strings.Fields(line)

	// Two required, positional fields: Name and iterations.
	if len(fields) < 2 {
		return nil, fmt.Errorf("two fields required, have %d", len(fields))
	}
	if !strings.HasPrefix(fields[0], "Benchmark") {
		return nil, fmt.Errorf(`first field does not start with "Benchmark"`)
	}
	n, err := strconv.Atoi(fields[1])
	if err != nil {
		return nil, err
	}
	b := &Benchmark{Name: fields[0], N: n}

	// Parse any remaining pairs of fields; we've parsed one pair already.
	for i := 1; i < len(fields)/2; i++ {
		b.parseMeasurement(fields[i*2], fields[i*2+1])
	}
	return b, nil
}

func (b *Benchmark) parseMeasurement(quant string, unit string) {
	switch unit {
	case "ns/op":
		if f, err := strconv.ParseFloat(quant, 64); err == nil {
			b.NsPerOp = f
			b.Measured |= NsPerOp
		}
	case "MB/s":
		if f, err := strconv.ParseFloat(quant, 64); err == nil {
			b.MBPerS = f
			b.Measured |= MBPerS
		}
	case "B/op":
		if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
			b.AllocedBytesPerOp = i
			b.Measured |= AllocedBytesPerOp
		}
	case "allocs/op":
		if i, err := strconv.ParseUint(quant, 10, 64); err == nil {
			b.AllocsPerOp = i
			b.Measured |= AllocsPerOp
		}
	}
}

func (b *Benchmark) String() string {
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%s %d", b.Name, b.N)
	if (b.Measured & NsPerOp) != 0 {
		fmt.Fprintf(buf, " %.2f ns/op", b.NsPerOp)
	}
	if (b.Measured & MBPerS) != 0 {
		fmt.Fprintf(buf, " %.2f MB/s", b.MBPerS)
	}
	if (b.Measured & AllocedBytesPerOp) != 0 {
		fmt.Fprintf(buf, " %d B/op", b.AllocedBytesPerOp)
	}
	if (b.Measured & AllocsPerOp) != 0 {
		fmt.Fprintf(buf, " %d allocs/op", b.AllocsPerOp)
	}
	return buf.String()
}

// Set is a collection of benchmarks from one
// testing.B run, keyed by name to facilitate comparison.
type Set map[string][]*Benchmark

// ParseSet extracts a Set from testing.B output.
// ParseSet preserves the order of benchmarks that have identical
// names.
func ParseSet(r io.Reader) (Set, error) {
	bb := make(Set)
	scan := bufio.NewScanner(r)
	ord := 0
	for scan.Scan() {
		if b, err := ParseLine(scan.Text()); err == nil {
			b.Ord = ord
			ord++
			bb[b.Name] = append(bb[b.Name], b)
		}
	}

	if err := scan.Err(); err != nil {
		return nil, err
	}

	return bb, nil
}