| // Copyright 2022 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 main |
| |
| import ( |
| "encoding/json" |
| "flag" |
| "fmt" |
| "os" |
| |
| // "github.com/dr2chase/debug-server/debug_client" |
| "golang.org/x/perf/benchfmt" |
| "golang.org/x/perf/benchseries" |
| // "runtime" |
| ) |
| |
| func main() { |
| // runtime.OnPanic(debug_client.TryDebug) |
| |
| bo := benchseries.BentBuilderOptions() |
| |
| var delta bool = false |
| var change bool = false |
| var values bool = true |
| var csv bool = true |
| var logScale bool = true |
| var boring bool = false |
| |
| var pngDir = "" |
| var svgDir = "" |
| var pdfDir = "" |
| |
| var jsonOut = "" |
| var jsonIn = "" |
| |
| confidence := 0.95 |
| threshold := 0.02 |
| |
| flag.StringVar(&bo.Series, "series", bo.Series, "Specify the benchmarking key for the series x-axis") |
| flag.StringVar(&bo.Experiment, "experiment", bo.Experiment, "Specify the experient-time key common to trials in an experiment") |
| |
| flag.StringVar(&bo.Compare, "compare", bo.Compare, "Specify the benchmark label/key used to select numerator and denominator in the comparison") |
| flag.StringVar(&bo.Numerator, "numerator", bo.Numerator, "Numerator value of -compare key") |
| flag.StringVar(&bo.Denominator, "denominator", bo.Denominator, "Denominator value of -compare key") |
| |
| flag.StringVar(&bo.NumeratorHash, "numerator-hash", bo.NumeratorHash, "Key for hash id of numerators (can be same as denominator-hash)") |
| flag.StringVar(&bo.DenominatorHash, "denominator-hash", bo.DenominatorHash, "Key for hash id of denominators (can be same as numerator-hash)") |
| |
| flag.StringVar(&bo.Filter, "filter", bo.Filter, "Apply this filter to incoming benchmarks") |
| |
| flag.BoolVar(&csv, "csv", csv, "Write the series in CSV form") |
| |
| flag.BoolVar(&delta, "delta", delta, "Include the plus-or-minus range in the spreadsheet view") |
| flag.BoolVar(&change, "change", change, "Include a change-detected column") |
| flag.BoolVar(&values, "values", values, "Include values columns") |
| flag.BoolVar(&logScale, "log", logScale, "Use a log scale in the chart") |
| |
| flag.StringVar(&pngDir, "png", pngDir, "Directory to write png chart(s) into") |
| flag.StringVar(&pdfDir, "pdf", pdfDir, "Directory to write pdf chart(s) into") |
| flag.StringVar(&svgDir, "svg", svgDir, "Directory to write svg chart(s) into") |
| |
| flag.StringVar(&jsonOut, "jo", jsonOut, "Save benchmarking summary in this json file") |
| flag.StringVar(&jsonIn, "ji", jsonIn, "Read benchmarking summary from this json file") |
| |
| flag.Float64Var(&confidence, "confidence", confidence, "width of confidence interval") |
| flag.Float64Var(&threshold, "threshold", threshold, "threshold for 'it changed' for exact metrics") |
| flag.BoolVar(&boring, "boring", boring, "include the boring parts of the history") |
| |
| flag.Parse() |
| |
| bo.Warn = warn |
| seriesBuilder, err := benchseries.NewBuilder(bo) |
| |
| if err != nil { |
| fail("%v\n", err) |
| } |
| |
| // Read supplied files |
| files := benchfmt.Files{Paths: flag.Args(), AllowStdin: true, AllowLabels: true} |
| err = seriesBuilder.AddFiles(files) |
| if err != nil { |
| fail("%v\n", err) |
| } |
| |
| // Optionally read JSON of pre-existing comparisons. |
| var comparisons []*benchseries.ComparisonSeries |
| |
| if jsonIn != "" { |
| f, err := os.Open(jsonIn) |
| if err != nil { |
| fail("Could not read JSON input file (flag -ji), %v", err) |
| } |
| decoder := json.NewDecoder(f) |
| decoder.Decode(&comparisons) |
| f.Close() |
| } |
| |
| // Rearrange into comparisons (not yet doing the statistical work) |
| comparisons, err = seriesBuilder.AllComparisonSeries(comparisons, benchseries.DUPE_REPLACE) |
| if err != nil { |
| fail("Error building comparison series: %v", err) |
| } |
| |
| // Chat about residues, per-table |
| for _, t := range comparisons { |
| fmt.Fprintf(os.Stderr, "%s residues=", t.Unit) |
| first := true |
| for _, r := range t.Residues { |
| if len(r.Slice) == 1 { |
| sep := "," |
| if first { |
| sep = "[" |
| first = false |
| } |
| fmt.Fprintf(os.Stderr, "%s%s=%s", sep, r.S, r.Slice[0]) |
| } |
| } |
| for _, r := range t.Residues { |
| if len(r.Slice) == 2 { |
| sep := "," |
| if first { |
| sep = "[" |
| first = false |
| } |
| fmt.Fprintf(os.Stderr, "%s%s={%s,%s}", sep, r.S, r.Slice[0], r.Slice[1]) |
| } else if len(r.Slice) > 2 { |
| sep := "," |
| if first { |
| sep = "[" |
| first = false |
| } |
| fmt.Fprintf(os.Stderr, "%slen(%s)=%d", sep, r.S, len(r.Slice)) |
| } |
| |
| } |
| fmt.Fprintf(os.Stderr, "]\n") |
| } |
| |
| // Bootstrap and add (missing, if some already supplied by JSON) summaries. |
| for _, c := range comparisons { |
| c.AddSummaries(confidence, 1000) |
| } |
| |
| // Generate some output. Options include CSV, JSON, PNG, perhaps also PDF and SVG. |
| |
| var options benchseries.CsvOptions |
| |
| if delta { |
| options |= benchseries.CSV_DELTA |
| } |
| if change { |
| options |= benchseries.CSV_CHANGE_HEU | benchseries.CSV_CHANGE_KS |
| } |
| if values { |
| options |= benchseries.CSV_VALUES |
| } |
| |
| if jsonOut != "" { |
| w, err := os.Create(jsonOut) |
| if err != nil { |
| fail("Could not create JSON output file (flag -jo), %v", err) |
| } |
| encoder := json.NewEncoder(w) |
| encoder.SetIndent("", "\t") |
| encoder.Encode(comparisons) |
| w.Close() |
| } |
| |
| if csv { |
| for _, comparison := range comparisons { |
| comparison.ToCsvBootstrapped(os.Stdout, options, threshold) |
| } |
| } |
| if pngDir != "" || pdfDir != "" || svgDir != "" { |
| benchseries.Chart(comparisons, pngDir, pdfDir, svgDir, logScale, threshold, boring) |
| } |
| } |
| |
| func fail(format string, args ...interface{}) { |
| fmt.Fprintf(os.Stderr, format, args...) |
| os.Exit(1) |
| } |
| func warn(format string, args ...interface{}) { |
| fmt.Fprintf(os.Stderr, format, args...) |
| } |