github.com/wI2L/jettison@v0.7.5-0.20230106001914-c70014c6417a/tools/benchparse/benchparse.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "flag" 7 "fmt" 8 "log" 9 "os" 10 "strconv" 11 "strings" 12 ) 13 14 var ( 15 omitMem = flag.Bool("omit-mem", false, "omit B/op stat") 16 omitAllocs = flag.Bool("omit-allocs", false, "omit allocs/op stat") 17 omitBandwidth = flag.Bool("omit-bandwidth", false, "omit MB/s stat") 18 flagInput = flag.String("in", "benchstats.csv", "csv-formatted benchstat file") 19 flagOutput = flag.String("out", "benchstats.json", "json-formatted data table file") 20 ) 21 22 func usage() { 23 fmt.Fprintf(os.Stderr, "usage: benchparse [options]\n") 24 fmt.Fprintf(os.Stderr, "options:\n") 25 flag.PrintDefaults() 26 os.Exit(2) 27 } 28 29 type set map[string][]benchStat 30 31 type benchStat struct { 32 Name string 33 Value float64 34 Unit string 35 } 36 37 type gcDataTables map[string][][]interface{} 38 39 func main() { 40 log.SetPrefix("benchparse: ") 41 log.SetFlags(0) 42 flag.Usage = usage 43 flag.Parse() 44 45 file, err := os.Open(*flagInput) 46 if err != nil { 47 log.Fatal(err) 48 } 49 defer file.Close() 50 51 stats, err := parse(bufio.NewScanner(file)) 52 if err != nil { 53 log.Fatal(err) 54 } 55 out, err := os.OpenFile(*flagOutput, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) 56 if err != nil { 57 log.Fatal(err) 58 } 59 defer out.Close() 60 61 data := transform(stats) 62 63 enc := json.NewEncoder(out) 64 if err := enc.Encode(data); err != nil { 65 log.Fatal(err) 66 } 67 if err := out.Sync(); err != nil { 68 log.Fatal(err) 69 } 70 os.Exit(0) 71 } 72 73 func parse(scanr *bufio.Scanner) (set, error) { 74 var ( 75 i int 76 unit string 77 stats = make(set) 78 ) 79 // The first line is always the header of the 80 // first section. All the next section headers 81 // will be detected thanks to the empty line 82 // that precede them. 83 nextIsHdr := true 84 85 for ; scanr.Scan(); i++ { 86 line := scanr.Text() 87 88 if len(line) == 0 { 89 nextIsHdr = true 90 continue 91 } 92 r := strings.Split(line, ",") 93 94 if nextIsHdr { 95 if len(r) != 2 { 96 return nil, fmt.Errorf("invalid header at line %d", i) 97 } 98 s := strings.Split(r[1], " ")[1] 99 unit = strings.Trim(s, "()") 100 nextIsHdr = false 101 continue 102 } 103 idx := strings.Index(r[0], "/") 104 if idx == -1 { 105 return nil, fmt.Errorf("invalid run name at line %d: no separator", i) 106 } 107 bname, rname := r[0][:idx], r[0][idx+1:] 108 if idx := strings.LastIndexByte(rname, '-'); idx != -1 { 109 rname = rname[:idx] 110 } 111 if _, ok := stats[bname]; !ok { 112 stats[bname] = nil 113 } 114 if len(r) <= 1 { 115 continue 116 } 117 f, err := strconv.ParseFloat(r[1], 64) 118 if err != nil { 119 return nil, fmt.Errorf("invalid float value at line %d: %s", i, err) 120 } 121 stats[bname] = append(stats[bname], benchStat{ 122 Name: rname, 123 Value: f, 124 Unit: unit, 125 }) 126 } 127 if err := scanr.Err(); err != nil { 128 return nil, err 129 } 130 return stats, nil 131 } 132 133 // transform converts a stats set to a 2D data-table 134 // interpretable by the Google Charts API. 135 func transform(stats set) gcDataTables { 136 data := make(gcDataTables) 137 138 for bname, stats := range stats { 139 values := make(map[string][]interface{}) 140 L: 141 for _, s := range stats { 142 if _, ok := values[s.Name]; !ok { 143 values[s.Name] = append(values[s.Name], s.Name) 144 } 145 switch s.Unit { 146 case "MB/s": 147 if *omitBandwidth { 148 continue L 149 } 150 case "B/op": // total memory allocated 151 if *omitMem { 152 continue L 153 } 154 case "allocs/op": // number of allocs 155 if *omitAllocs { 156 continue L 157 } 158 } 159 values[s.Name] = append(values[s.Name], s.Value) 160 } 161 data[bname] = append(data[bname], []interface{}{"Name", "ns/op"}) 162 if !*omitBandwidth { 163 data[bname][0] = append(data[bname][0], "MB/s") 164 } 165 if !*omitMem { 166 data[bname][0] = append(data[bname][0], "B/op") 167 } 168 if !*omitAllocs { 169 data[bname][0] = append(data[bname][0], "allocs/op") 170 } 171 for _, v := range values { 172 data[bname] = append(data[bname], v) 173 } 174 } 175 return data 176 }