Skip to content

Commit accf19b

Browse files
authored
Add better benchstat (#24)
1 parent cba3519 commit accf19b

File tree

4 files changed

+357
-1
lines changed

4 files changed

+357
-1
lines changed

benchstat.go

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"flag"
7+
"fmt"
8+
"log"
9+
"math"
10+
"os"
11+
"strings"
12+
13+
"golang.org/x/perf/benchstat"
14+
)
15+
16+
func runBenchstat(args []string) error {
17+
fset := flag.NewFlagSet("benchstat", flag.ContinueOnError)
18+
flagDeltaTest := fset.String("delta-test", "utest", "significance `test` to apply to delta: utest, ttest, or none")
19+
flagAlpha := fset.Float64("alpha", 0.05, "consider change significant if p < `α`")
20+
flagGeomean := fset.Bool("geomean", false, "print the geometric mean of each file")
21+
flagSplit := fset.String("split", "pkg,goos,goarch", "split benchmarks by `labels`")
22+
flagSort := fset.String("sort", "none", "sort by `order`: [-]delta, [-]name, none")
23+
noColor := fset.Bool("no-color", false, "disable the colored output")
24+
if err := fset.Parse(args); err != nil {
25+
return err
26+
}
27+
args = fset.Args()
28+
29+
colorsEnabled := !*noColor
30+
31+
var deltaTestNames = map[string]benchstat.DeltaTest{
32+
"none": benchstat.NoDeltaTest,
33+
"u": benchstat.UTest,
34+
"u-test": benchstat.UTest,
35+
"utest": benchstat.UTest,
36+
"t": benchstat.TTest,
37+
"t-test": benchstat.TTest,
38+
"ttest": benchstat.TTest,
39+
}
40+
41+
var sortNames = map[string]benchstat.Order{
42+
"none": nil,
43+
"name": benchstat.ByName,
44+
"delta": benchstat.ByDelta,
45+
}
46+
47+
deltaTest := deltaTestNames[strings.ToLower(*flagDeltaTest)]
48+
if deltaTest == nil {
49+
return fmt.Errorf("invalid delta-test argument: %q", *flagDeltaTest)
50+
}
51+
52+
reverse := false
53+
sortName := *flagSort
54+
if strings.HasPrefix(sortName, "-") {
55+
reverse = true
56+
sortName = sortName[1:]
57+
}
58+
59+
order, ok := sortNames[sortName]
60+
if !ok {
61+
return fmt.Errorf("invalid sort argument: %q", sortName)
62+
}
63+
64+
if len(fset.Args()) == 0 {
65+
// TODO(oleg): print command help here?
66+
return errors.New("expected at least 1 positional argument, the benchmarking target")
67+
}
68+
69+
c := &benchstat.Collection{
70+
Alpha: *flagAlpha,
71+
AddGeoMean: *flagGeomean,
72+
DeltaTest: deltaTest,
73+
}
74+
75+
if *flagSplit != "" {
76+
c.SplitBy = strings.Split(*flagSplit, ",")
77+
}
78+
79+
if order != nil {
80+
if reverse {
81+
order = benchstat.Reverse(order)
82+
}
83+
c.Order = order
84+
}
85+
86+
fmt.Printf("args: %+v", args)
87+
for _, file := range args {
88+
f, err := os.Open(file)
89+
if err != nil {
90+
return err
91+
}
92+
if err := c.AddFile(file, f); err != nil {
93+
return err
94+
}
95+
f.Close()
96+
}
97+
98+
tables := c.Tables()
99+
fixBenchstatTables(tables)
100+
if colorsEnabled {
101+
colorizeBenchstatTables(tables)
102+
}
103+
104+
var buf bytes.Buffer
105+
benchstat.FormatText(&buf, tables)
106+
os.Stdout.Write(buf.Bytes())
107+
108+
return nil
109+
}
110+
111+
func fixBenchstatTables(tables []*benchstat.Table) {
112+
disabledGeomean := map[string]struct{}{}
113+
for _, table := range tables {
114+
selectedRows := table.Rows[:0]
115+
for _, row := range table.Rows {
116+
if row.PctDelta == 0 && strings.Contains(row.Delta, "0.00%") {
117+
// For whatever reason, sometimes we get +0.00% results
118+
// in delta which will be painted red. This is misleading.
119+
// Let's replace +0.00% with tilde.
120+
row.Delta = "~"
121+
}
122+
for _, m := range row.Metrics {
123+
for _, v := range m.RValues {
124+
if v < 0.01 {
125+
disabledGeomean[m.Unit] = struct{}{}
126+
}
127+
}
128+
}
129+
if row.Benchmark == "[Geo mean]" {
130+
if len(row.Metrics) != 0 {
131+
_, disabled := disabledGeomean[row.Metrics[0].Unit]
132+
if disabled {
133+
continue
134+
}
135+
}
136+
}
137+
selectedRows = append(selectedRows, row)
138+
if len(row.Metrics) == 0 {
139+
continue
140+
}
141+
if len(row.Metrics[0].RValues) < 5 && row.Benchmark != "[Geo mean]" {
142+
log.Printf("WARNING: %s needs more samples, re-run with -count=5 or higher?", row.Benchmark)
143+
}
144+
}
145+
table.Rows = selectedRows
146+
}
147+
}
148+
149+
func colorizeBenchstatTables(tables []*benchstat.Table) {
150+
for _, table := range tables {
151+
for _, row := range table.Rows {
152+
if isEpsilonDelta(row.Metrics) {
153+
row.Delta = yellowColorize("~")
154+
continue
155+
}
156+
157+
d := calculateCombinedMeanDiff(row.Metrics)
158+
if isTinyValue(row.Metrics) {
159+
d *= 2 // For tiny values, require x2 precision.
160+
}
161+
162+
d++
163+
if math.Abs(row.PctDelta) < d {
164+
row.Delta = yellowColorize("~")
165+
continue
166+
}
167+
168+
switch {
169+
case strings.HasPrefix(row.Delta, "+"):
170+
row.Delta = redColorize(row.Delta)
171+
case strings.HasPrefix(row.Delta, "-"):
172+
row.Delta = greenColorize(row.Delta)
173+
default:
174+
row.Delta = yellowColorize(row.Delta)
175+
}
176+
}
177+
}
178+
}
179+
180+
func isEpsilonDelta(metrics []*benchstat.Metrics) bool {
181+
if len(metrics) != 2 {
182+
return false
183+
}
184+
185+
eps := getValueEpsilon(avgValue(metrics))
186+
m0 := metrics[0].Mean
187+
m1 := metrics[1].Mean
188+
return math.Abs(m0-m1) <= eps
189+
}
190+
191+
func avgValue(metrics []*benchstat.Metrics) float64 {
192+
var sum float64
193+
for _, m := range metrics {
194+
sum += m.Mean
195+
}
196+
return sum / float64(len(metrics))
197+
}
198+
199+
func getValueEpsilon(avg float64) float64 {
200+
switch {
201+
case avg < 10:
202+
return 1
203+
case avg < 32:
204+
return 2
205+
case avg < 80:
206+
return 3
207+
default:
208+
return 4
209+
}
210+
}
211+
212+
func calculateCombinedMeanDiff(metrics []*benchstat.Metrics) float64 {
213+
var sum float64
214+
for _, m := range metrics {
215+
if m.Max != m.Min {
216+
sum += 100.0 * calculateMeanDiff(m)
217+
}
218+
}
219+
return sum
220+
}
221+
222+
func calculateMeanDiff(m *benchstat.Metrics) float64 {
223+
if m.Mean == 0 || m.Max == 0 {
224+
return 0
225+
}
226+
227+
diff := 1 - m.Min/m.Mean
228+
if d := m.Max/m.Mean - 1; d > diff {
229+
diff = d
230+
}
231+
return diff
232+
}
233+
234+
func isTinyValue(metrics []*benchstat.Metrics) bool {
235+
const tinyValueThreshold = 32.0 // in nanosecs
236+
237+
for _, m := range metrics {
238+
if m.Mean >= tinyValueThreshold {
239+
return false
240+
}
241+
}
242+
return true
243+
}
244+
245+
func redColorize(s string) string { return "\033[31m" + s + "\033[0m" }
246+
func greenColorize(s string) string { return "\033[32m" + s + "\033[0m" }
247+
func yellowColorize(s string) string { return "\033[33m" + s + "\033[0m" }

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/go-perf/go-perftuner
22

33
go 1.10
44

5-
require github.com/cristalhq/acmd v0.11.1
5+
require (
6+
github.com/cristalhq/acmd v0.11.1
7+
golang.org/x/perf v0.0.0-20230221235046-aebcfb61e84c
8+
)

go.sum

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,100 @@
1+
cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2+
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
3+
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
4+
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
5+
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
6+
github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
7+
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
8+
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
9+
github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
10+
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
11+
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
112
github.com/cristalhq/acmd v0.11.1 h1:DJ4fh2Pv0nPKmqT646IU/0Vh5FNdGblxvF+3/W3NAUI=
213
github.com/cristalhq/acmd v0.11.1/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ=
14+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15+
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
16+
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
17+
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
18+
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
19+
github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
20+
github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
21+
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
22+
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
23+
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
24+
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
25+
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
26+
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
27+
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
28+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
29+
github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
30+
github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
31+
github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
32+
github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
33+
github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
34+
github.com/google/safehtml v0.0.2 h1:ZOt2VXg4x24bW0m2jtzAOkhoXV0iM8vNKc0paByCZqM=
35+
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
36+
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
37+
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
38+
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
39+
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
40+
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
41+
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
42+
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
43+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
44+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
45+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
46+
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
47+
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
48+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
49+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
50+
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
51+
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
52+
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
53+
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
54+
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
55+
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
56+
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
57+
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
58+
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
59+
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
60+
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
61+
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
62+
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
63+
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
64+
golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
65+
golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
66+
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
67+
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
68+
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
69+
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
70+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
71+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
72+
golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
73+
golang.org/x/perf v0.0.0-20230221235046-aebcfb61e84c h1:xR7iBj/IHpQuWFrftdwa1ttoVJKSDg+37NHA2XblmrU=
74+
golang.org/x/perf v0.0.0-20230221235046-aebcfb61e84c/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc=
75+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
76+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
77+
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
78+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
79+
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
80+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
81+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
82+
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
83+
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
84+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
85+
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
86+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
87+
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
88+
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
89+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
90+
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
91+
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
92+
gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
93+
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
94+
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
95+
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
96+
gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ=
97+
google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
98+
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
99+
google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
100+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ var cmds = []acmd.Command{
7575
return run(&funcSizeRunner{})
7676
},
7777
},
78+
{
79+
Name: "benchstat",
80+
Alias: "bs",
81+
Description: "stricter benchstat with colors",
82+
ExecFunc: func(_ context.Context, args []string) error {
83+
return runBenchstat(args)
84+
},
85+
},
7886
}
7987

8088
type subCommandRunner interface {

0 commit comments

Comments
 (0)