@@ -3,6 +3,7 @@ package main
33import (
44 "context"
55 "encoding/csv"
6+ "errors"
67 "flag"
78 "fmt"
89 "log"
@@ -31,13 +32,6 @@ type config struct {
3132 update bool
3233}
3334
34- type benchResult struct {
35- name string
36- timePerOp float64
37- bytesPerOp int64
38- allocsPerOp int64
39- }
40-
4135func main () {
4236 cfg := parseFlags ()
4337
@@ -48,7 +42,10 @@ func main() {
4842 }
4943
5044 if ! isBenchstatAvailable () {
51- log .Fatalf ("benchstat not found in PATH; install golang.org/x/perf/cmd/benchstat@%s in your environment or run with --install-benchstat" , benchstatVersion )
45+ log .Fatalf (
46+ "benchstat not found in PATH; install golang.org/x/perf/cmd/benchstat@%s in your environment or run with --install-benchstat" ,
47+ benchstatVersion ,
48+ )
5249 }
5350
5451 if len (cfg .targetDirs ) == 0 {
@@ -79,16 +76,36 @@ func parseFlags() config {
7976 flag .StringVar (& cfg .benchtime , "benchtime" , "1s" , "benchmark time" )
8077 flag .IntVar (& cfg .count , "count" , 5 , "number of benchmark runs" )
8178 flag .BoolVar (& cfg .benchmem , "benchmem" , false , "include memory allocation stats" )
82- flag .Float64Var (& cfg .threshold , "threshold" , 1.5 , "performance regression threshold (e.g., 1.5 = 50% slower)" )
83- flag .BoolVar (& cfg .installTool , "install-benchstat" , false , "install benchstat tool if not found" )
79+ flag .Float64Var (
80+ & cfg .threshold ,
81+ "threshold" ,
82+ 1.5 ,
83+ "performance regression threshold (e.g., 1.5 = 50% slower)" ,
84+ )
85+ flag .BoolVar (
86+ & cfg .installTool ,
87+ "install-benchstat" ,
88+ false ,
89+ "install benchstat tool if not found" ,
90+ )
8491 flag .BoolVar (& cfg .verbose , "verbose" , false , "verbose output" )
8592 flag .BoolVar (& cfg .update , "update" , false , "update baseline files instead of comparing" )
8693
8794 var failMetricsStr string
88- flag .StringVar (& failMetricsStr , "fail-metrics" , "" , "comma-separated list of metrics to check for regressions (default: time/op)" )
95+ flag .StringVar (
96+ & failMetricsStr ,
97+ "fail-metrics" ,
98+ "" ,
99+ "comma-separated list of metrics to check for regressions (default: time/op)" ,
100+ )
89101
90102 var targetDirsStr string
91- flag .StringVar (& targetDirsStr , "target-dirs" , "" , "comma-separated list of directories to benchmark" )
103+ flag .StringVar (
104+ & targetDirsStr ,
105+ "target-dirs" ,
106+ "" ,
107+ "comma-separated list of directories to benchmark" ,
108+ )
92109
93110 flag .Parse ()
94111
@@ -107,37 +124,41 @@ func parseFlags() config {
107124
108125func installBenchstat () error {
109126 fmt .Printf ("Installing benchstat@%s...\n " , benchstatVersion )
110- cmd := exec .Command ("go" , "install" , fmt . Sprintf ( "golang.org/x/perf/cmd/benchstat@%s" , benchstatVersion ) )
127+ cmd := exec .Command ("go" , "install" , "golang.org/x/perf/cmd/benchstat@" + benchstatVersion )
111128 cmd .Stdout = os .Stdout
112129 cmd .Stderr = os .Stderr
130+
113131 return cmd .Run ()
114132}
115133
116134func isBenchstatAvailable () bool {
117135 _ , err := exec .LookPath ("benchstat" )
136+
118137 return err == nil
119138}
120139
121140func findBenchmarkDirs () []string {
122141 var dirs []string
123142
124- err := filepath .WalkDir ("internal/namespaces" , func (path string , d os.DirEntry , err error ) error {
125- if err != nil {
126- return err
127- }
128-
129- if d .IsDir () {
130- return nil
131- }
143+ err := filepath .WalkDir (
144+ "internal/namespaces" ,
145+ func (path string , d os.DirEntry , err error ) error {
146+ if err != nil {
147+ return err
148+ }
132149
133- if strings .HasSuffix (d .Name (), "_benchmark_test.go" ) {
134- dir := filepath .Dir (path )
135- dirs = append (dirs , dir )
136- }
150+ if d .IsDir () {
151+ return nil
152+ }
137153
138- return nil
139- })
154+ if strings .HasSuffix (d .Name (), "_benchmark_test.go" ) {
155+ dir := filepath .Dir (path )
156+ dirs = append (dirs , dir )
157+ }
140158
159+ return nil
160+ },
161+ )
141162 if err != nil {
142163 log .Printf ("error scanning for benchmark directories: %v" , err )
143164 }
@@ -162,6 +183,7 @@ func runBenchmarksForDir(cfg config, dir string) error {
162183 return fmt .Errorf ("failed to update baseline: %w" , err )
163184 }
164185 fmt .Printf ("✅ Baseline updated: %s\n " , baselineFile )
186+
165187 return nil
166188 }
167189
@@ -172,6 +194,7 @@ func runBenchmarksForDir(cfg config, dir string) error {
172194 return fmt .Errorf ("failed to save baseline: %w" , err )
173195 }
174196 fmt .Printf ("Baseline saved to %s\n " , baselineFile )
197+
175198 return nil
176199 }
177200
@@ -180,7 +203,12 @@ func runBenchmarksForDir(cfg config, dir string) error {
180203}
181204
182205func runBenchmarks (cfg config , dir string ) (string , error ) {
183- args := []string {"test" , "-bench=" + cfg .bench , "-benchtime=" + cfg .benchtime , "-count=" + strconv .Itoa (cfg .count )}
206+ args := []string {
207+ "test" ,
208+ "-bench=" + cfg .bench ,
209+ "-benchtime=" + cfg .benchtime ,
210+ "-count=" + strconv .Itoa (cfg .count ),
211+ }
184212
185213 if cfg .benchmem {
186214 args = append (args , "-benchmem" )
@@ -208,11 +236,11 @@ func runBenchmarks(cfg config, dir string) (string, error) {
208236
209237func saveBaseline (filename , content string ) error {
210238 dir := filepath .Dir (filename )
211- if err := os .MkdirAll (dir , 0755 ); err != nil {
239+ if err := os .MkdirAll (dir , 0o755 ); err != nil {
212240 return err
213241 }
214242
215- return os .WriteFile (filename , []byte (content ), 0644 )
243+ return os .WriteFile (filename , []byte (content ), 0o644 )
216244}
217245
218246func compareWithBaseline (cfg config , baselineFile , newResults string ) error {
@@ -233,7 +261,12 @@ func compareWithBaseline(cfg config, baselineFile, newResults string) error {
233261 cmd := exec .Command ("benchstat" , "-format=csv" , baselineFile , tmpFile .Name ())
234262 output , err := cmd .CombinedOutput ()
235263 if err != nil {
236- return fmt .Errorf ("failed to compare with benchstat for %s: %w\n Output: %s" , filepath .Dir (baselineFile ), err , output )
264+ return fmt .Errorf (
265+ "failed to compare with benchstat for %s: %w\n Output: %s" ,
266+ filepath .Dir (baselineFile ),
267+ err ,
268+ output ,
269+ )
237270 }
238271
239272 // Parse CSV output and check for regressions
@@ -249,6 +282,7 @@ func checkForRegressions(cfg config, csvOutput string) error {
249282
250283 if len (records ) < 2 {
251284 fmt .Println ("No benchmark comparisons found" )
285+
252286 return nil
253287 }
254288
@@ -263,7 +297,7 @@ func checkForRegressions(cfg config, csvOutput string) error {
263297 newAllocsIdx := findColumnIndex (header , "new allocs/op" )
264298
265299 if nameIdx == - 1 {
266- return fmt . Errorf ("could not find 'name' column in benchstat output" )
300+ return errors . New ("could not find 'name' column in benchstat output" )
267301 }
268302
269303 var regressions []string
@@ -278,7 +312,10 @@ func checkForRegressions(cfg config, csvOutput string) error {
278312 // Check time/op regression
279313 if contains (cfg .failMetrics , "time/op" ) && oldTimeIdx != - 1 && newTimeIdx != - 1 {
280314 if regression := checkMetricRegression (record , oldTimeIdx , newTimeIdx , cfg .threshold ); regression != "" {
281- regressions = append (regressions , fmt .Sprintf ("%s: time/op %s" , benchName , regression ))
315+ regressions = append (
316+ regressions ,
317+ fmt .Sprintf ("%s: time/op %s" , benchName , regression ),
318+ )
282319 }
283320 }
284321
@@ -292,7 +329,10 @@ func checkForRegressions(cfg config, csvOutput string) error {
292329 // Check allocs/op regression
293330 if contains (cfg .failMetrics , "allocs/op" ) && oldAllocsIdx != - 1 && newAllocsIdx != - 1 {
294331 if regression := checkMetricRegression (record , oldAllocsIdx , newAllocsIdx , cfg .threshold ); regression != "" {
295- regressions = append (regressions , fmt .Sprintf ("%s: allocs/op %s" , benchName , regression ))
332+ regressions = append (
333+ regressions ,
334+ fmt .Sprintf ("%s: allocs/op %s" , benchName , regression ),
335+ )
296336 }
297337 }
298338
@@ -310,10 +350,15 @@ func checkForRegressions(cfg config, csvOutput string) error {
310350 for _ , regression := range regressions {
311351 fmt .Printf (" - %s\n " , regression )
312352 }
313- return fmt .Errorf ("performance regressions detected" )
353+
354+ return errors .New ("performance regressions detected" )
314355 }
315356
316- fmt .Printf ("✅ No significant performance regressions detected (threshold: %.1fx)\n " , cfg .threshold )
357+ fmt .Printf (
358+ "✅ No significant performance regressions detected (threshold: %.1fx)\n " ,
359+ cfg .threshold ,
360+ )
361+
317362 return nil
318363}
319364
@@ -323,6 +368,7 @@ func findColumnIndex(header []string, columnName string) int {
323368 return i
324369 }
325370 }
371+
326372 return - 1
327373}
328374
@@ -354,7 +400,7 @@ func parseMetricValue(s string) (float64, error) {
354400 s = strings .TrimSpace (s )
355401
356402 if s == "" || s == "-" {
357- return 0 , fmt . Errorf ("empty value" )
403+ return 0 , errors . New ("empty value" )
358404 }
359405
360406 return strconv .ParseFloat (s , 64 )
@@ -366,5 +412,6 @@ func contains(slice []string, item string) bool {
366412 return true
367413 }
368414 }
415+
369416 return false
370417}
0 commit comments