github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/benchmany/readlog.go (about) 1 // Copyright 2015 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 main 6 7 import ( 8 "io/ioutil" 9 "log" 10 "strconv" 11 "strings" 12 13 "github.com/aclements/go-moremath/stats" 14 ) 15 16 // ComputeStats updates the derived statistics in s from the raw 17 // samples in s.Values. 18 func (stat *Benchstat) ComputeStats() { 19 stat.Mean = stats.Mean(stat.Values) 20 } 21 22 // A Benchstat is the metrics along one axis (e.g., ns/op or MB/s) 23 // for all runs of a specific benchmark. 24 type Benchstat struct { 25 Unit string 26 Values []float64 // metrics 27 Mean float64 // mean of Values 28 } 29 30 // A BenchKey identifies one metric (e.g., "ns/op", "B/op") from one 31 // benchmark (function name sans "Benchmark" prefix) in one 32 // configuration (input file name). 33 type BenchKey struct { 34 Config, Benchmark, Unit string 35 } 36 37 type Collection struct { 38 Stats map[BenchKey]*Benchstat 39 40 // Keys gives all keys of Stats in the order added. 41 Keys []BenchKey 42 43 // Configs, Benchmarks, and Units give the set of configs, 44 // benchmarks, and units from the keys in Stats in an order 45 // meant to match the order the benchmarks were read in. 46 Configs, Benchmarks, Units []string 47 48 // ConfigSet, BenchmarkSet, and UnitSet are set 49 // representations of Configs, Benchmarks, and Units. 50 ConfigSet, BenchmarkSet, UnitSet map[string]bool 51 } 52 53 func (c *Collection) AddStat(key BenchKey) *Benchstat { 54 if stat, ok := c.Stats[key]; ok { 55 return stat 56 } 57 58 c.addKey(key) 59 stat := &Benchstat{Unit: key.Unit} 60 c.Stats[key] = stat 61 return stat 62 } 63 64 func (c *Collection) addKey(key BenchKey) { 65 addString := func(strings *[]string, set map[string]bool, add string) { 66 if set[add] { 67 return 68 } 69 *strings = append(*strings, add) 70 set[add] = true 71 } 72 c.Keys = append(c.Keys, key) 73 addString(&c.Configs, c.ConfigSet, key.Config) 74 addString(&c.Benchmarks, c.BenchmarkSet, key.Benchmark) 75 addString(&c.Units, c.UnitSet, key.Unit) 76 } 77 78 func (c *Collection) Filter(key BenchKey) *Collection { 79 c2 := NewCollection() 80 for _, k := range c.Keys { 81 if (key.Config == "" || key.Config == k.Config) && 82 (key.Benchmark == "" || key.Benchmark == k.Benchmark) && 83 (key.Unit == "" || key.Unit == k.Unit) { 84 c2.addKey(k) 85 c2.Stats[k] = c.Stats[k] 86 } 87 } 88 return c2 89 } 90 91 func NewCollection() *Collection { 92 return &Collection{ 93 Stats: make(map[BenchKey]*Benchstat), 94 ConfigSet: make(map[string]bool), 95 BenchmarkSet: make(map[string]bool), 96 UnitSet: make(map[string]bool), 97 } 98 } 99 100 // readFiles reads a set of benchmark files as a Collection. 101 func readFiles(files ...string) *Collection { 102 c := NewCollection() 103 for _, file := range files { 104 readFile(file, c) 105 } 106 return c 107 } 108 109 var unitOfXMetric = map[string]string{ 110 "time": "ns/op", 111 "allocated": "allocated bytes/op", // ΔMemStats.TotalAlloc / N 112 "allocs": "allocs/op", // ΔMemStats.Mallocs / N 113 "sys-total": "bytes from system", // MemStats.Sys 114 "sys-heap": "heap bytes from system", // MemStats.HeapSys 115 "sys-stack": "stack bytes from system", // MemStats.StackSys 116 "sys-gc": "GC bytes from system", // MemStats.GCSys 117 "sys-other": "other bytes from system", // MemStats.OtherSys+MSpanSys+MCacheSys+BuckHashSys 118 "gc-pause-total": "STW ns/op", // ΔMemStats.PauseTotalNs / N 119 "gc-pause-one": "STW ns/GC", // ΔMemStats.PauseTotalNs / ΔNumGC 120 "rss": "max RSS bytes", // Rusage.Maxrss * 1<<10 121 "cputime": "user+sys ns/op", // Rusage.Utime+Stime 122 "virtual-mem": "peak VM bytes", // /proc/self/status VmPeak 123 } 124 125 // readFile reads a set of benchmarks from a file in to a Collection. 126 func readFile(file string, c *Collection) { 127 c.Configs = append(c.Configs, file) 128 key := BenchKey{Config: file} 129 130 text, err := ioutil.ReadFile(file) 131 if err != nil { 132 log.Fatal(err) 133 } 134 for _, line := range strings.Split(string(text), "\n") { 135 if strings.HasPrefix(line, "GOPERF-METRIC:") { 136 // x/benchmarks-style output. 137 line := line[14:] 138 f := strings.Split(line, "=") 139 val, err := strconv.ParseFloat(f[1], 64) 140 if err != nil { 141 continue 142 } 143 key.Benchmark = f[0] 144 key.Unit = unitOfXMetric[f[0]] 145 if key.Unit == "" { 146 continue 147 } 148 stat := c.AddStat(key) 149 stat.Values = append(stat.Values, val) 150 continue 151 } 152 153 f := strings.Fields(line) 154 if len(f) < 4 { 155 continue 156 } 157 name := f[0] 158 if !strings.HasPrefix(name, "Benchmark") { 159 continue 160 } 161 name = strings.TrimPrefix(name, "Benchmark") 162 n, _ := strconv.Atoi(f[1]) 163 if n == 0 { 164 continue 165 } 166 167 key.Benchmark = name 168 for i := 2; i+2 <= len(f); i += 2 { 169 val, err := strconv.ParseFloat(f[i], 64) 170 if err != nil { 171 continue 172 } 173 key.Unit = f[i+1] 174 stat := c.AddStat(key) 175 stat.Values = append(stat.Values, val) 176 } 177 } 178 }