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// compare_benchmarks takes the JSON-formatted output of bssl speed and 16// compares it against a baseline output. 17package main 18 19import ( 20 "encoding/json" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "os" 25) 26 27var baselineFile = flag.String("baseline", "", "the path to the JSON file containing the base results") 28 29type Result struct { 30 Description string `json:"description"` 31 NumCalls int `json:"numCalls"` 32 Microseconds int `json:"microseconds"` 33 BytesPerCall int `json:"bytesPerCall"` 34} 35 36func (r *Result) Speed() (float64, string) { 37 callsPerSecond := float64(r.NumCalls) / float64(r.Microseconds) * 1000000 38 if r.BytesPerCall == 0 { 39 return callsPerSecond, "ops/sec" 40 } 41 return callsPerSecond * float64(r.BytesPerCall) / 1000000, "MB/sec" 42} 43 44func printResult(result Result, baseline *Result) error { 45 if baseline != nil { 46 if result.Description != baseline.Description { 47 return fmt.Errorf("result did not match baseline: %q vs %q", result.Description, baseline.Description) 48 } 49 50 if result.BytesPerCall != baseline.BytesPerCall { 51 return fmt.Errorf("result %q bytes per call did not match baseline: %d vs %d", result.Description, result.BytesPerCall, baseline.BytesPerCall) 52 } 53 } 54 55 newSpeed, unit := result.Speed() 56 fmt.Printf("Did %d %s operations in %dus (%.1f %s)", result.NumCalls, result.Description, result.Microseconds, newSpeed, unit) 57 if baseline != nil { 58 oldSpeed, _ := baseline.Speed() 59 fmt.Printf(" [%+.1f%%]", (newSpeed-oldSpeed)/oldSpeed*100) 60 } 61 fmt.Printf("\n") 62 return nil 63} 64 65func readResults(path string) ([]Result, error) { 66 data, err := ioutil.ReadFile(path) 67 if err != nil { 68 return nil, err 69 } 70 var ret []Result 71 if err := json.Unmarshal(data, &ret); err != nil { 72 return nil, err 73 } 74 return ret, nil 75} 76 77func main() { 78 flag.Parse() 79 80 baseline, err := readResults(*baselineFile) 81 if err != nil { 82 fmt.Fprintf(os.Stderr, "Error reading %q: %s\n", *baselineFile, err) 83 os.Exit(1) 84 } 85 86 fmt.Println(*baselineFile) 87 for _, result := range baseline { 88 if err := printResult(result, nil); err != nil { 89 fmt.Fprintf(os.Stderr, "Error in %q: %s\n", *baselineFile, err) 90 os.Exit(1) 91 } 92 } 93 94 for _, arg := range flag.Args() { 95 results, err := readResults(arg) 96 if err != nil { 97 fmt.Fprintf(os.Stderr, "Error reading %q: %s\n", arg, err) 98 os.Exit(1) 99 } 100 101 if len(results) != len(baseline) { 102 fmt.Fprintf(os.Stderr, "Result files %q and %q have different lengths\n", arg, *baselineFile) 103 os.Exit(1) 104 } 105 106 fmt.Printf("\n%s\n", arg) 107 for i, result := range results { 108 if err := printResult(result, &baseline[i]); err != nil { 109 fmt.Fprintf(os.Stderr, "Error in %q: %s\n", arg, err) 110 os.Exit(1) 111 } 112 } 113 } 114} 115