Skip to content

Commit

Permalink
benchproc: add an end-to-end example
Browse files Browse the repository at this point in the history
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
aclements committed Mar 17, 2022
1 parent f944feb commit 7f87d66
Show file tree
Hide file tree
Showing 2 changed files with 567 additions and 0 deletions.
141 changes: 141 additions & 0 deletions benchproc/example_test.go
@@ -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
}

0 comments on commit 7f87d66

Please sign in to comment.