cmd/bench: add -pgo flag
This change adds a flag to cmd/bench to do a few runs to collect
profiles and feed that back into the build before doing measured
benchmark runs.
This mode is only supported in Sweet to begin with.
Change-Id: I483ca5d5385b341dee4038641e16e02f2ce3493d
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/594776
Auto-Submit: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/cmd/bench/bent.go b/cmd/bench/bent.go
index 25152fe..783de55 100644
--- a/cmd/bench/bent.go
+++ b/cmd/bench/bent.go
@@ -63,7 +63,11 @@
return os.RemoveAll(dir)
}
-func bent(tcs []*toolchain) (err error) {
+func bent(tcs []*toolchain, pgo bool) (err error) {
+ if pgo {
+ log.Printf("Skipping bent benchmarks (PGO not supported)")
+ return nil
+ }
dir, err := os.MkdirTemp("", "bent")
if err != nil {
return fmt.Errorf("error creating temporary directory: %w", err)
diff --git a/cmd/bench/gotest.go b/cmd/bench/gotest.go
index de3dab7..6575bbc 100644
--- a/cmd/bench/gotest.go
+++ b/cmd/bench/gotest.go
@@ -10,7 +10,11 @@
"path/filepath"
)
-func goTest(tcs []*toolchain) error {
+func goTest(tcs []*toolchain, pgo bool) error {
+ if pgo {
+ log.Printf("Skipping Go test benchmarks (PGO not supported)")
+ return nil
+ }
for _, tc := range tcs {
log.Printf("Running Go test benchmarks for %s", tc.Name)
fmt.Printf("toolchain: %s\n", tc.Name)
diff --git a/cmd/bench/main.go b/cmd/bench/main.go
index 18f81e4..7da8fe9 100644
--- a/cmd/bench/main.go
+++ b/cmd/bench/main.go
@@ -24,6 +24,7 @@
var (
wait = flag.Bool("wait", true, "wait for system idle before starting benchmarking")
+ pgo = flag.Bool("pgo", false, "run the benchmarks to collect profiles and rebuild them with PGO enabled before measuring")
gorootExperiment = flag.String("goroot", "", "GOROOT to test (default $GOROOT or 'go env GOROOT')")
gorootBaseline = flag.String("goroot-baseline", "", "baseline GOROOT to test against (optional) (default $BENCH_BASELINE_GOROOT)")
branch = flag.String("branch", "", "branch of the commits we're testing against (default $BENCH_BRANCH or unknown)")
@@ -64,16 +65,17 @@
}
}
-func run(tcs []*toolchain) error {
+func run(tcs []*toolchain, pgo bool) error {
// Because each of the functions below is responsible for running
// benchmarks under each toolchain itself, it is also responsible
// for ensuring that the benchmark tag "toolchain" is printed.
pass := true
- if err := goTest(tcs); err != nil {
+
+ if err := goTest(tcs, pgo); err != nil {
pass = false
log.Printf("Error running Go tests: %v", err)
}
- if err := bent(tcs); err != nil {
+ if err := bent(tcs, pgo); err != nil {
pass = false
log.Printf("Error running bent: %v", err)
}
@@ -93,7 +95,7 @@
return fmt.Errorf("failed to clean Go cache: %w", err)
}
}
- if err := sweet(tcs); err != nil {
+ if err := sweet(tcs, pgo); err != nil {
pass = false
log.Printf("Error running sweet: %v", err)
}
@@ -189,7 +191,7 @@
return
}
// Run benchmarks against the toolchains.
- if err := run(toolchains); err != nil {
+ if err := run(toolchains, *pgo); err != nil {
log.Print("FAIL")
os.Exit(1)
}
diff --git a/cmd/bench/sweet.go b/cmd/bench/sweet.go
index c0375e1..575d005 100644
--- a/cmd/bench/sweet.go
+++ b/cmd/bench/sweet.go
@@ -39,7 +39,7 @@
return nil
}
-func sweet(tcs []*toolchain) (err error) {
+func sweet(tcs []*toolchain, pgo bool) (err error) {
tmpDir, err := os.MkdirTemp("", "go-sweet")
if err != nil {
return fmt.Errorf("error creating temporary directory: %w", err)
@@ -103,16 +103,20 @@
// Finally we can actually run the benchmarks.
resultsDir := filepath.Join(tmpDir, "results")
workDir := filepath.Join(tmpDir, "work")
- cmd = exec.Command(
- sweetBin, "run",
+ sweetRunArgs := []string{
+ "run",
"-run", "default",
"-count", "10",
"-bench-dir", filepath.Join(sweetRoot, "benchmarks"),
"-cache", assetsCacheDir,
"-work-dir", workDir,
"-results", resultsDir,
- confFile,
- )
+ }
+ if pgo {
+ sweetRunArgs = append(sweetRunArgs, "-pgo")
+ }
+ sweetRunArgs = append(sweetRunArgs, confFile)
+ cmd = exec.Command(sweetBin, sweetRunArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -129,7 +133,26 @@
}
}()
- // Dump results to stdout.
+ dumpResults := func(filename string) error {
+ // Print pkg and shortname tags because Sweet won't do it.
+ benchName := filepath.Base(filepath.Dir(filename))
+ fmt.Printf("pkg: golang.org/x/benchmarks/sweet/benchmarks/%s\n", benchName)
+ fmt.Printf("shortname: sweet_%s\n", strings.ReplaceAll(benchName, "-", "_"))
+
+ // Dump results file.
+ f, err := os.Open(filename)
+ if err != nil {
+ return fmt.Errorf("opening result %s: %v", filename, err)
+ }
+ defer f.Close()
+ if _, err := io.Copy(os.Stdout, f); err != nil {
+ return fmt.Errorf("reading result %s: %v", filename, err)
+ }
+ return nil
+ }
+
+ // Dump non-PGO results to stdout.
+ fmt.Printf("pgo: off\n")
for _, tc := range tcs {
matches, err := filepath.Glob(filepath.Join(resultsDir, "*", fmt.Sprintf("%s.results", tc.Name)))
if err != nil {
@@ -137,21 +160,26 @@
}
fmt.Printf("toolchain: %s\n", tc.Name)
for _, match := range matches {
- // Print pkg and shortname tags because Sweet won't do it.
- benchName := filepath.Base(filepath.Dir(match))
- fmt.Printf("pkg: golang.org/x/benchmarks/sweet/benchmarks/%s\n", benchName)
- fmt.Printf("shortname: sweet_%s\n", strings.ReplaceAll(benchName, "-", "_"))
+ if err := dumpResults(match); err != nil {
+ return err
+ }
+ }
+ }
- // Dump results file.
- f, err := os.Open(match)
+ // Dump PGO results to stdout, if we expect them to exist.
+ if pgo {
+ fmt.Printf("pgo: on\n")
+ for _, tc := range tcs {
+ matches, err := filepath.Glob(filepath.Join(resultsDir, "*", fmt.Sprintf("%s.pgo.results", tc.Name)))
if err != nil {
- return fmt.Errorf("opening result %s: %v", match, err)
+ return fmt.Errorf("searching for PGO results for %s in %s: %v", tc.Name, resultsDir, err)
}
- if _, err := io.Copy(os.Stdout, f); err != nil {
- f.Close()
- return fmt.Errorf("reading result %s: %v", match, err)
+ fmt.Printf("toolchain: %s\n", tc.Name)
+ for _, match := range matches {
+ if err := dumpResults(match); err != nil {
+ return err
+ }
}
- f.Close()
}
}
return nil