blob: 8f1c263e605084d11514f9607df617e5bef34fb5 [file] [log] [blame]
David Chase64dc4392022-03-14 18:40:31 -04001// Copyright 2022 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
5package benchseries
6
7import (
8 "encoding/csv"
9 "fmt"
10 "io"
11 "math"
12 "sort"
13
14 "golang.org/x/perf/benchmath"
15)
16
17type CsvOptions int
18
19const (
20 CSV_DISTRIBUTION_BITS CsvOptions = 3
21 CSV_PLAIN CsvOptions = 0
22 CSV_DELTA CsvOptions = 1
23 CSV_LOHI CsvOptions = 2
24
25 CSV_VALUES CsvOptions = 4
26
27 CSV_CHANGE_HEU CsvOptions = 8 // This is the interval-overlap heuristic
28 CSV_CHANGE_KS CsvOptions = 16 // This is a Kolmogorov-Smirnov statistic
29)
30
31func (cs *ComparisonSeries) ToCsvBootstrapped(out io.Writer, options CsvOptions, threshold float64) {
32 tab, entries := cs.headerAndEntries(options)
33 summaries := cs.Summaries
34 for i, s := range cs.Series {
35 row := []string{s}
36 changesHeu := []float64{}
37 changesKs := []float64{}
38 for j, b := range cs.Benchmarks {
39 clear(entries)
40 // if _, ok := cs.SummaryAt(b, s); ok {
41 _ = b
42 if sum := summaries[i][j]; sum.Defined() {
43 center, low, high := sum.Center, sum.Low, sum.High
44
45 entries[0] = strof(center)
46 entries = entries[:1]
47
48 if i > 0 && summaries[i-1][j].Defined() {
49 p := summaries[i-1][j]
50 ch := p.HeurOverlap(sum, threshold)
51 changesHeu = append(changesHeu, ch)
52 cks := p.KSov(sum)
53 changesKs = append(changesKs, cks)
54
55 if options&CSV_CHANGE_HEU != 0 {
56 if !(math.IsInf(ch, 0) || math.IsNaN(ch)) {
57 entries = append(entries, strof(ch))
58 } else {
59 entries = append(entries, "")
60 }
61 }
62 if options&CSV_CHANGE_KS != 0 {
63 if !(math.IsInf(cks, 0) || math.IsNaN(cks)) {
64 entries = append(entries, strof(cks))
65 } else {
66 entries = append(entries, "")
67 }
68 }
69 } else {
70 if options&CSV_CHANGE_HEU != 0 {
71 entries = append(entries, "")
72 }
73 if options&CSV_CHANGE_KS != 0 {
74 entries = append(entries, "")
75 }
76 }
77
78 switch options & CSV_DISTRIBUTION_BITS {
79 case CSV_PLAIN:
80 case CSV_DELTA:
81 entries = append(entries, pctof((high-low)/(2*center)))
82 case CSV_LOHI:
83 entries = append(entries, strof(low), strof(high))
84 }
85 }
86 row = append(row, entries...)
87 }
88 changes := func(a []float64) []string {
89 sort.Float64s(a)
90 return []string{strof(percentile(a, .50)), strof(percentile(a, .75)), strof(percentile(a, .90)), strof(percentile(a, 1)), strof(norm(a, 1)), strof(norm(a, 2))}
91 }
92 if options&CSV_CHANGE_HEU != 0 {
93 row = append(row, changes(changesHeu)...)
94 }
95 if options&CSV_CHANGE_KS != 0 {
96 row = append(row, changes(changesKs)...)
97 }
98 tab = append(tab, row)
99 }
100 csvw := csv.NewWriter(out)
101 csvw.WriteAll(tab)
102 csvw.Flush()
103}
104
105func (cs *ComparisonSeries) headerAndEntries(options CsvOptions) (tab [][]string, entries []string) {
106 entriesLen := 1
107 switch options & CSV_DISTRIBUTION_BITS {
108 case CSV_PLAIN:
109 case CSV_DELTA:
110 entriesLen += 1
111 case CSV_LOHI:
112 entriesLen += 2
113 }
114 if options&CSV_CHANGE_HEU != 0 {
115 entriesLen += 6
116 }
117 if options&CSV_CHANGE_KS != 0 {
118 entriesLen += 6
119 }
120
121 entries = make([]string, entriesLen, entriesLen) // ratio, change, +/- or (lo, hi)
122
123 hdr := []string{cs.Unit}
124 for _, b := range cs.Benchmarks {
125 hdr = append(hdr, b)
126 if options&CSV_CHANGE_HEU != 0 {
127 hdr = append(hdr, "change_heur")
128 }
129 if options&CSV_CHANGE_KS != 0 {
130 hdr = append(hdr, "change_ks")
131 }
132 switch options & CSV_DISTRIBUTION_BITS {
133 case CSV_PLAIN:
134 case CSV_DELTA:
135 hdr = append(hdr, "±")
136 case CSV_LOHI:
137 hdr = append(hdr, "lo", "hi")
138 }
139 }
140
141 if options&CSV_CHANGE_HEU != 0 {
142 hdr = append(hdr, "change_heur .5", "change_heur .75", "change_heur .9", "change_heur max", "change_heur avg", "change_heur rms")
143 }
144
145 if options&CSV_CHANGE_KS != 0 {
146 hdr = append(hdr, "change_ks .5", "change_ks .75", "change_ks .9", "change_ks max", "change_ks avg", "change_ks rms")
147 }
148
149 tab = [][]string{hdr}
150 return
151}
152
153func clear(entries []string) {
154 for i := range entries {
155 entries[i] = ""
156 }
157}
158
159func percentPlusOrMinus(sum benchmath.Summary) float64 {
160 return 100 * (sum.Hi - sum.Lo) / (2 * sum.Center)
161}
162
163func strof(x float64) string {
164 return fmt.Sprintf("%f", x)
165}
166
167func pctof(x float64) string {
168 return fmt.Sprintf("%f%%", x)
169}