| // 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 | |
| } |