1// Copyright (c) 2020 Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15//go:build ignore 16 17// compare_benchmarks takes the JSON-formatted output of bssl speed and 18// compares it against a baseline output. 19package main 20 21import ( 22 "encoding/json" 23 "flag" 24 "fmt" 25 "os" 26) 27 28var baselineFile = flag.String("baseline", "", "the path to the JSON file containing the base results") 29 30type Result struct { 31 Description string `json:"description"` 32 NumCalls int `json:"numCalls"` 33 Microseconds int `json:"microseconds"` 34 BytesPerCall int `json:"bytesPerCall"` 35} 36 37func (r *Result) Speed() (float64, string) { 38 callsPerSecond := float64(r.NumCalls) / float64(r.Microseconds) * 1000000 39 if r.BytesPerCall == 0 { 40 return callsPerSecond, "ops/sec" 41 } 42 return callsPerSecond * float64(r.BytesPerCall) / 1000000, "MB/sec" 43} 44 45func printResult(result Result, baseline *Result) error { 46 if baseline != nil { 47 if result.Description != baseline.Description { 48 return fmt.Errorf("result did not match baseline: %q vs %q", result.Description, baseline.Description) 49 } 50 51 if result.BytesPerCall != baseline.BytesPerCall { 52 return fmt.Errorf("result %q bytes per call did not match baseline: %d vs %d", result.Description, result.BytesPerCall, baseline.BytesPerCall) 53 } 54 } 55 56 newSpeed, unit := result.Speed() 57 fmt.Printf("Did %d %s operations in %dus (%.1f %s)", result.NumCalls, result.Description, result.Microseconds, newSpeed, unit) 58 if baseline != nil { 59 oldSpeed, _ := baseline.Speed() 60 fmt.Printf(" [%+.1f%%]", (newSpeed-oldSpeed)/oldSpeed*100) 61 } 62 fmt.Printf("\n") 63 return nil 64} 65 66func readResults(path string) ([]Result, error) { 67 data, err := os.ReadFile(path) 68 if err != nil { 69 return nil, err 70 } 71 var ret []Result 72 if err := json.Unmarshal(data, &ret); err != nil { 73 return nil, err 74 } 75 return ret, nil 76} 77 78func main() { 79 flag.Parse() 80 81 baseline, err := readResults(*baselineFile) 82 if err != nil { 83 fmt.Fprintf(os.Stderr, "Error reading %q: %s\n", *baselineFile, err) 84 os.Exit(1) 85 } 86 87 fmt.Println(*baselineFile) 88 for _, result := range baseline { 89 if err := printResult(result, nil); err != nil { 90 fmt.Fprintf(os.Stderr, "Error in %q: %s\n", *baselineFile, err) 91 os.Exit(1) 92 } 93 } 94 95 for _, arg := range flag.Args() { 96 results, err := readResults(arg) 97 if err != nil { 98 fmt.Fprintf(os.Stderr, "Error reading %q: %s\n", arg, err) 99 os.Exit(1) 100 } 101 102 if len(results) != len(baseline) { 103 fmt.Fprintf(os.Stderr, "Result files %q and %q have different lengths\n", arg, *baselineFile) 104 os.Exit(1) 105 } 106 107 fmt.Printf("\n%s\n", arg) 108 for i, result := range results { 109 if err := printResult(result, &baseline[i]); err != nil { 110 fmt.Fprintf(os.Stderr, "Error in %q: %s\n", arg, err) 111 os.Exit(1) 112 } 113 } 114 } 115} 116