github.com/jgbaldwinbrown/perf@v0.1.1/benchproc/example_test.go (about)

     1  // 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  
     5  package benchproc
     6  
     7  import (
     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.
    18  func 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  }