k3s/vendor/github.com/cespare/prettybench/prettybench.go

199 lines
4.5 KiB
Go

package main
import (
"bufio"
"bytes"
"errors"
"flag"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
bench "golang.org/x/tools/benchmark/parse"
)
var noPassthrough = flag.Bool("no-passthrough", false, "Don't print non-benchmark lines")
type BenchOutputGroup struct {
Lines []*bench.Benchmark
// Columns which are in use
Measured int
}
type Table struct {
MaxLengths []int
Cells [][]string
}
func (g *BenchOutputGroup) String() string {
if len(g.Lines) == 0 {
return ""
}
columnNames := []string{"benchmark", "iter", "time/iter"}
if (g.Measured & bench.MBPerS) > 0 {
columnNames = append(columnNames, "throughput")
}
if (g.Measured & bench.AllocedBytesPerOp) > 0 {
columnNames = append(columnNames, "bytes alloc")
}
if (g.Measured & bench.AllocsPerOp) > 0 {
columnNames = append(columnNames, "allocs")
}
table := &Table{Cells: [][]string{columnNames}}
var underlines []string
for _, name := range columnNames {
underlines = append(underlines, strings.Repeat("-", len(name)))
}
table.Cells = append(table.Cells, underlines)
timeFormatFunc := g.TimeFormatFunc()
for _, line := range g.Lines {
row := []string{line.Name, FormatIterations(line.N), timeFormatFunc(line.NsPerOp)}
if (g.Measured & bench.MBPerS) > 0 {
row = append(row, FormatMegaBytesPerSecond(line))
}
if (g.Measured & bench.AllocedBytesPerOp) > 0 {
row = append(row, FormatBytesAllocPerOp(line))
}
if (g.Measured & bench.AllocsPerOp) > 0 {
row = append(row, FormatAllocsPerOp(line))
}
table.Cells = append(table.Cells, row)
}
for i := range columnNames {
maxLength := 0
for _, row := range table.Cells {
if len(row[i]) > maxLength {
maxLength = len(row[i])
}
}
table.MaxLengths = append(table.MaxLengths, maxLength)
}
var buf bytes.Buffer
for _, row := range table.Cells {
for i, cell := range row {
var format string
switch i {
case 0:
format = "%%-%ds "
case len(row) - 1:
format = "%%%ds"
default:
format = "%%%ds "
}
fmt.Fprintf(&buf, fmt.Sprintf(format, table.MaxLengths[i]), cell)
}
fmt.Fprint(&buf, "\n")
}
return buf.String()
}
func FormatIterations(iter int) string {
return strconv.FormatInt(int64(iter), 10)
}
func (g *BenchOutputGroup) TimeFormatFunc() func(float64) string {
// Find the smallest time
smallest := g.Lines[0].NsPerOp
for _, line := range g.Lines[1:] {
if line.NsPerOp < smallest {
smallest = line.NsPerOp
}
}
switch {
case smallest < float64(10000*time.Nanosecond):
return func(ns float64) string {
return fmt.Sprintf("%.2f ns/op", ns)
}
case smallest < float64(time.Millisecond):
return func(ns float64) string {
return fmt.Sprintf("%.2f μs/op", ns/1000)
}
case smallest < float64(10*time.Second):
return func(ns float64) string {
return fmt.Sprintf("%.2f ms/op", (ns / 1e6))
}
default:
return func(ns float64) string {
return fmt.Sprintf("%.2f s/op", ns/1e9)
}
}
}
func FormatMegaBytesPerSecond(l *bench.Benchmark) string {
if (l.Measured & bench.MBPerS) == 0 {
return ""
}
return fmt.Sprintf("%.2f MB/s", l.MBPerS)
}
func FormatBytesAllocPerOp(l *bench.Benchmark) string {
if (l.Measured & bench.AllocedBytesPerOp) == 0 {
return ""
}
return fmt.Sprintf("%d B/op", l.AllocedBytesPerOp)
}
func FormatAllocsPerOp(l *bench.Benchmark) string {
if (l.Measured & bench.AllocsPerOp) == 0 {
return ""
}
return fmt.Sprintf("%d allocs/op", l.AllocsPerOp)
}
func (g *BenchOutputGroup) AddLine(line *bench.Benchmark) {
g.Lines = append(g.Lines, line)
g.Measured |= line.Measured
}
var (
benchLineMatcher = regexp.MustCompile(`^Benchmark.*\t.*\d+`)
okLineMatcher = regexp.MustCompile(`^ok\s`)
notBenchLineErr = errors.New("Not a bench line")
)
func ParseLine(line string) (*bench.Benchmark, error) {
if !benchLineMatcher.MatchString(line) {
return nil, notBenchLineErr
}
fields := strings.Split(line, "\t")
if len(fields) < 3 {
return nil, notBenchLineErr
}
return bench.ParseLine(line)
}
func main() {
flag.Parse()
currentBenchmark := &BenchOutputGroup{}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
text := scanner.Text()
line, err := ParseLine(text)
switch err {
case notBenchLineErr:
if okLineMatcher.MatchString(text) {
fmt.Print(currentBenchmark)
currentBenchmark = &BenchOutputGroup{}
}
if !*noPassthrough {
fmt.Println(text)
}
case nil:
currentBenchmark.AddLine(line)
default:
fmt.Fprintln(os.Stderr, "prettybench unrecognized line:")
fmt.Println(text)
}
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}