blob: 04a8adb936d9efe423f99276daf9c0bfba6ac46f [file] [log] [blame]
Austin Clements7f87d662021-10-08 13:10:28 -04001// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package benchproc
6
7import (
8 "fmt"
9 "log"
10 "os"
11
12 "golang.org/x/perf/benchfmt"
13 "golang.org/x/perf/benchunit"
14)
15
16// Example shows a complete benchmark processing pipeline that uses
17// filtering, projection, accumulation, and sorting.
18func Example() {
19 // Open the example benchmark data.
20 f, err := os.Open("testdata/suffixarray.bench")
21 if err != nil {
22 log.Fatal(err)
23 }
24 defer f.Close()
25
26 // Create a filter that extracts just "BenchmarkNew" on the value
27 // "go" of the name key "text". Typically, the filter expression
28 // would come from a command-line flag.
29 filter, err := NewFilter(".name:New /text:go")
30 if err != nil {
31 log.Fatal(err)
32 }
33 // Create a projection. This projection extracts "/bits=" and
34 // "/size=" from the benchmark name. It sorts bits in the
35 // default, first-observation order and size numerically.
36 // Typically, the projection expression would come from a
37 // command-line flag.
38 var pp ProjectionParser
39 projection, err := pp.Parse("/bits,/size@num", filter)
40 if err != nil {
41 log.Fatal(err)
42 }
43 // Create a projection that captures all configuration not
44 // captured by the above projection. We'll use this to check
45 // if there's unexpected variation in other configuration
46 // fields and report it.
47 residue := pp.Residue()
48
49 // We'll accumulate benchmark results by their projection.
50 // Projections create Keys, which are == if the projected
51 // values are ==, so they can be used as map keys.
52 bySize := make(map[Key][]float64)
53 var keys []Key
54 var residues []Key
55
56 // Read the benchmark results.
57 r := benchfmt.NewReader(f, "example")
58 for r.Scan() {
59 var res *benchfmt.Result
60 switch rec := r.Result(); rec := rec.(type) {
61 case *benchfmt.Result:
62 res = rec
63 case *benchfmt.SyntaxError:
64 // Report a non-fatal parse error.
65 log.Print(err)
66 continue
67 default:
68 // Unknown record type. Ignore.
69 continue
70 }
71
72 // Step 1: If necessary, transform the Result, for example to
73 // add configuration keys that could be used in filters and
74 // projections. This example doesn't need any transformation.
75
76 // Step 2: Filter the result.
77 if match, err := filter.Apply(res); !match {
78 // Result was fully excluded by the filter.
79 if err != nil {
80 // Print the reason we rejected this result.
81 log.Print(err)
82 }
83 continue
84 }
85
86 // Step 3: Project the result. This produces a Key
87 // that captures the "size" and "bits" from the result.
88 key := projection.Project(res)
89
90 // Accumulate the results by configuration.
91 speed, ok := res.Value("sec/op")
92 if !ok {
93 continue
94 }
95 if _, ok := bySize[key]; !ok {
96 keys = append(keys, key)
97 }
98 bySize[key] = append(bySize[key], speed)
99
100 // Collect residue configurations.
101 resConfig := residue.Project(res)
102 residues = append(residues, resConfig)
103 }
104 // Check for I/O errors.
105 if err := r.Err(); err != nil {
106 log.Fatal(err)
107 }
108
109 // Step 4: Sort the collected configurations using the order
110 // specified by the projection.
111 SortKeys(keys)
112
113 // Print the results.
114 fmt.Printf("%-24s %s\n", "config", "sec/op")
115 for _, config := range keys {
116 fmt.Printf("%-24s %s\n", config, benchunit.Scale(mean(bySize[config]), benchunit.Decimal))
117 }
118
119 // Check if there was variation in any other configuration
120 // fields that wasn't captured by the projection and warn the
121 // user that something may be unexpected.
122 nonsingular := NonSingularFields(residues)
123 if len(nonsingular) > 0 {
124 fmt.Printf("warning: results vary in %s\n", nonsingular)
125 }
126
127 // Output:
128 // config sec/op
129 // /bits:32 /size:100K 4.650m
130 // /bits:32 /size:500K 26.18m
131 // /bits:32 /size:1M 51.39m
132 // /bits:32 /size:5M 306.7m
133 // /bits:32 /size:10M 753.0m
134 // /bits:32 /size:50M 5.814
135 // /bits:64 /size:100K 5.081m
136 // /bits:64 /size:500K 26.43m
137 // /bits:64 /size:1M 55.60m
138 // /bits:64 /size:5M 366.6m
139 // /bits:64 /size:10M 821.2m
140 // /bits:64 /size:50M 6.390
141}