cmd/bench: add PGO sweet runs

Sweet represents PGO runs as a unique configuration (using
`name+".pgo"`), effectively a unique "toolchain". But it is really a
separate dimension. So when reporting results, we use the same
"toolchain" for both and add the new "pgo" key that reports whether PGO
was enabled or not.

CL 498335 adds backend parsing of the results and bumps the builder
timeout for the extra runtime.

Change-Id: Iea1c4fe81b7d2a35bc0cf15c1a1be87c87f2a90b
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/498278
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/bench/sweet.go b/cmd/bench/sweet.go
index 41e6762..b759ea1 100644
--- a/cmd/bench/sweet.go
+++ b/cmd/bench/sweet.go
@@ -107,6 +107,7 @@
 		sweetBin, "run",
 		"-run", "all",
 		"-count", "10",
+		"-pgo",
 		"-bench-dir", filepath.Join(sweetRoot, "benchmarks"),
 		"-cache", assetsCacheDir,
 		"-work-dir", workDir,
@@ -131,28 +132,47 @@
 
 	// Dump results to stdout.
 	for _, tc := range tcs {
-		matches, err := filepath.Glob(filepath.Join(resultsDir, "*", fmt.Sprintf("%s.results", tc.Name)))
-		if err != nil {
-			return fmt.Errorf("searching for results for %s in %s: %v", tc.Name, resultsDir, err)
-		}
 		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, "-", "_"))
 
-			// Dump results file.
-			f, err := os.Open(match)
-			if err != nil {
-				return fmt.Errorf("opening result %s: %v", match, err)
-			}
-			if _, err := io.Copy(os.Stdout, f); err != nil {
-				f.Close()
-				return fmt.Errorf("reading result %s: %v", match, err)
-			}
-			f.Close()
+		fmt.Printf("pgo: off\n")
+		if err := dumpResults(resultsDir, tc.Name); err != nil {
+			return fmt.Errorf("error dumping standard results: %v", err)
+		}
+
+		fmt.Printf("pgo: on\n")
+		if err := dumpResults(resultsDir, tc.Name+".pgo"); err != nil {
+			return fmt.Errorf("error dumping PGO results: %v", err)
 		}
 	}
+
+	// Reset keys for good measure.
+	fmt.Printf("toolchain:\n")
+	fmt.Printf("pgo:\n")
+	return nil
+}
+
+func dumpResults(resultsDir, name string) error {
+	matches, err := filepath.Glob(filepath.Join(resultsDir, "*", fmt.Sprintf("%s.results", name)))
+	if err != nil {
+		return fmt.Errorf("searching for results for %s in %s: %v", name, resultsDir, err)
+	}
+	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, "-", "_"))
+
+		// Dump results file.
+		f, err := os.Open(match)
+		if err != nil {
+			return fmt.Errorf("opening result %s: %v", match, err)
+		}
+		if _, err := io.Copy(os.Stdout, f); err != nil {
+			f.Close()
+			return fmt.Errorf("reading result %s: %v", match, err)
+		}
+		f.Close()
+	}
+
 	return nil
 }