Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
benchproc: add an end-to-end example
Change-Id: I4c401c43b1255ded311bede92ce70fc8f7f5feaf Reviewed-on: https://go-review.googlesource.com/c/perf/+/355169 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Russ Cox <rsc@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com>
- Loading branch information
Showing
2 changed files
with
567 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright 2021 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 benchproc | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
|
||
"golang.org/x/perf/benchfmt" | ||
"golang.org/x/perf/benchunit" | ||
) | ||
|
||
// Example shows a complete benchmark processing pipeline that uses | ||
// filtering, projection, accumulation, and sorting. | ||
func Example() { | ||
// Open the example benchmark data. | ||
f, err := os.Open("testdata/suffixarray.bench") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer f.Close() | ||
|
||
// Create a filter that extracts just "BenchmarkNew" on the value | ||
// "go" of the name key "text". Typically, the filter expression | ||
// would come from a command-line flag. | ||
filter, err := NewFilter(".name:New /text:go") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
// Create a projection. This projection extracts "/bits=" and | ||
// "/size=" from the benchmark name. It sorts bits in the | ||
// default, first-observation order and size numerically. | ||
// Typically, the projection expression would come from a | ||
// command-line flag. | ||
var pp ProjectionParser | ||
projection, err := pp.Parse("/bits,/size@num", filter) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
// Create a projection that captures all configuration not | ||
// captured by the above projection. We'll use this to check | ||
// if there's unexpected variation in other configuration | ||
// fields and report it. | ||
residue := pp.Residue() | ||
|
||
// We'll accumulate benchmark results by their projection. | ||
// Projections create Keys, which are == if the projected | ||
// values are ==, so they can be used as map keys. | ||
bySize := make(map[Key][]float64) | ||
var keys []Key | ||
var residues []Key | ||
|
||
// Read the benchmark results. | ||
r := benchfmt.NewReader(f, "example") | ||
for r.Scan() { | ||
var res *benchfmt.Result | ||
switch rec := r.Result(); rec := rec.(type) { | ||
case *benchfmt.Result: | ||
res = rec | ||
case *benchfmt.SyntaxError: | ||
// Report a non-fatal parse error. | ||
log.Print(err) | ||
continue | ||
default: | ||
// Unknown record type. Ignore. | ||
continue | ||
} | ||
|
||
// Step 1: If necessary, transform the Result, for example to | ||
// add configuration keys that could be used in filters and | ||
// projections. This example doesn't need any transformation. | ||
|
||
// Step 2: Filter the result. | ||
if match, err := filter.Apply(res); !match { | ||
// Result was fully excluded by the filter. | ||
if err != nil { | ||
// Print the reason we rejected this result. | ||
log.Print(err) | ||
} | ||
continue | ||
} | ||
|
||
// Step 3: Project the result. This produces a Key | ||
// that captures the "size" and "bits" from the result. | ||
key := projection.Project(res) | ||
|
||
// Accumulate the results by configuration. | ||
speed, ok := res.Value("sec/op") | ||
if !ok { | ||
continue | ||
} | ||
if _, ok := bySize[key]; !ok { | ||
keys = append(keys, key) | ||
} | ||
bySize[key] = append(bySize[key], speed) | ||
|
||
// Collect residue configurations. | ||
resConfig := residue.Project(res) | ||
residues = append(residues, resConfig) | ||
} | ||
// Check for I/O errors. | ||
if err := r.Err(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Step 4: Sort the collected configurations using the order | ||
// specified by the projection. | ||
SortKeys(keys) | ||
|
||
// Print the results. | ||
fmt.Printf("%-24s %s\n", "config", "sec/op") | ||
for _, config := range keys { | ||
fmt.Printf("%-24s %s\n", config, benchunit.Scale(mean(bySize[config]), benchunit.Decimal)) | ||
} | ||
|
||
// Check if there was variation in any other configuration | ||
// fields that wasn't captured by the projection and warn the | ||
// user that something may be unexpected. | ||
nonsingular := NonSingularFields(residues) | ||
if len(nonsingular) > 0 { | ||
fmt.Printf("warning: results vary in %s\n", nonsingular) | ||
} | ||
|
||
// Output: | ||
// config sec/op | ||
// /bits:32 /size:100K 4.650m | ||
// /bits:32 /size:500K 26.18m | ||
// /bits:32 /size:1M 51.39m | ||
// /bits:32 /size:5M 306.7m | ||
// /bits:32 /size:10M 753.0m | ||
// /bits:32 /size:50M 5.814 | ||
// /bits:64 /size:100K 5.081m | ||
// /bits:64 /size:500K 26.43m | ||
// /bits:64 /size:1M 55.60m | ||
// /bits:64 /size:5M 366.6m | ||
// /bits:64 /size:10M 821.2m | ||
// /bits:64 /size:50M 6.390 | ||
} |
Oops, something went wrong.