Skip to content

Commit 1ba3a21

Browse files
prattmicgopherbot
authored andcommitted
benchseries: ignore results with mismatched compare value
Currently, benchseries assumes that results with compare value == denominator is in the denominator set, and everything else is in the numerator. In addition to being confusing (BuilderOptions.Numerator is completely ignored), this breaks things when the compare key isn't set at all. In that case cmpCfg.StringValues() is always "", so everything ends up in the numerator and nothing in the denominator, which causes a later crash due to a missing denominator [1]. Be more explicit and only save results that explicitly fall into the numerator or denominator. [1] This fix isn't perfect, as benchmark results that contain only numerator results and no denominator results will still cause a crash. That should probably be an error or simply result in no comparisons. For golang/go#61362. Change-Id: I1444d0fcb101bcea0f1cdf31de7292211dcbc901 Reviewed-on: https://go-review.googlesource.com/c/perf/+/510475 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com> Run-TryBot: Michael Pratt <mpratt@google.com> Reviewed-by: David Chase <drchase@google.com>
1 parent d343f63 commit 1ba3a21

File tree

2 files changed

+200
-5
lines changed

2 files changed

+200
-5
lines changed

benchseries/benchseries.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -362,27 +362,33 @@ func (b *Builder) Add(result *benchfmt.Result) {
362362
newCell := func() *Cell {
363363
return &Cell{Residues: make(map[benchproc.Key]struct{})}
364364
}
365-
if cmpCfg.StringValues() == b.denCompareVal {
365+
switch cmpCfg.StringValues() {
366+
case b.denCompareVal:
366367
c = t.baseline
367368
if c == nil {
368369
c = newCell()
369370
t.baseline = c
370371
t.baselineHash = denHashCfg
371372
t.baselineHashString = denHashCfg.StringValues()
372373
}
373-
} else {
374+
case b.numCompareVal:
374375
c = t.tests[numHashCfg]
375376
if c == nil {
376377
c = newCell()
377378
t.tests[numHashCfg] = c
378379
b.hashToOrder[numHashCfg] = serCfg
379380
}
381+
default:
382+
// Compare field is set to an unknown value or
383+
// completely unset. Don't add cell.
380384
}
381385

382386
// Add to the cell.
383-
c.Values = append(c.Values, result.Values[unitI].Value)
384-
c.Residues[residueCfg] = struct{}{}
385-
b.Residues[residueCfg] = struct{}{}
387+
if c != nil {
388+
c.Values = append(c.Values, result.Values[unitI].Value)
389+
c.Residues[residueCfg] = struct{}{}
390+
b.Residues[residueCfg] = struct{}{}
391+
}
386392
}
387393
}
388394

benchseries/benchseries_test.go

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package benchseries
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"testing"
7+
8+
"golang.org/x/perf/benchfmt"
9+
)
10+
11+
func makeConfig(key, value string) benchfmt.Config {
12+
return benchfmt.Config{
13+
Key: key,
14+
Value: []byte(value),
15+
File: true, // N.B. Residue ignores non-File config!
16+
}
17+
}
18+
19+
func TestBasic(t *testing.T) {
20+
results := []*benchfmt.Result{
21+
{
22+
Config: []benchfmt.Config{
23+
makeConfig("compare", "numerator"),
24+
makeConfig("runstamp", "2020-01-01T00:00:00Z"),
25+
makeConfig("numerator-hash", "abcdef0123456789"),
26+
makeConfig("denominator-hash", "9876543219fedcba"),
27+
makeConfig("numerator-hash-time", "2020-02-02T00:00:00Z"),
28+
makeConfig("residue", "foo"),
29+
},
30+
Name: []byte("Foo"),
31+
Iters: 10,
32+
Values: []benchfmt.Value{
33+
{Value: 1, Unit: "sec"},
34+
},
35+
},
36+
{
37+
Config: []benchfmt.Config{
38+
makeConfig("compare", "denominator"),
39+
makeConfig("runstamp", "2020-01-01T00:00:00Z"),
40+
makeConfig("numerator-hash", "abcdef0123456789"),
41+
makeConfig("denominator-hash", "9876543219fedcba"),
42+
makeConfig("numerator-hash-time", "2020-02-02T00:00:00Z"),
43+
makeConfig("residue", "foo"),
44+
},
45+
Name: []byte("Foo"),
46+
Iters: 10,
47+
Values: []benchfmt.Value{
48+
{Value: 10, Unit: "sec"},
49+
},
50+
},
51+
}
52+
53+
opts := &BuilderOptions{
54+
Filter: ".unit:/.*/",
55+
Series: "numerator-hash-time",
56+
Table: "", // .unit only
57+
Experiment: "runstamp",
58+
Compare: "compare",
59+
Numerator: "numerator",
60+
Denominator: "denominator",
61+
NumeratorHash: "numerator-hash",
62+
DenominatorHash: "denominator-hash",
63+
Warn: func(format string, args ...interface{}) {
64+
s := fmt.Sprintf(format, args...)
65+
t.Errorf("benchseries warning: %s", s)
66+
},
67+
}
68+
builder, err := NewBuilder(opts)
69+
if err != nil {
70+
t.Fatalf("NewBuilder get err %v, want err nil", err)
71+
}
72+
73+
for _, r := range results {
74+
builder.Add(r)
75+
}
76+
77+
comparisons, err := builder.AllComparisonSeries(nil, DUPE_REPLACE)
78+
if err != nil {
79+
t.Fatalf("AllComparisonSeries got err %v want err nil", err)
80+
}
81+
82+
if len(comparisons) != 1 {
83+
t.Fatalf("len(comparisons) got %d want 1; comparisons: %+v", len(comparisons), comparisons)
84+
}
85+
86+
got := comparisons[0]
87+
88+
if got.Unit != "sec" {
89+
t.Errorf(`Unit got %q, want "sec"`, got.Unit)
90+
}
91+
92+
wantBenchmarks := []string{"Foo"}
93+
if !reflect.DeepEqual(got.Benchmarks, wantBenchmarks) {
94+
t.Errorf("Benchmarks got %v want %v", got.Benchmarks, wantBenchmarks)
95+
}
96+
97+
wantSeries := []string{"2020-02-02T00:00:00+00:00"}
98+
if !reflect.DeepEqual(got.Series, wantSeries) {
99+
t.Errorf("Series got %v want %v", got.Series, wantSeries)
100+
}
101+
102+
wantHashPairs := map[string]ComparisonHashes{
103+
"2020-02-02T00:00:00+00:00": ComparisonHashes{
104+
NumHash: "abcdef0123456789",
105+
DenHash: "9876543219fedcba",
106+
},
107+
}
108+
if !reflect.DeepEqual(got.HashPairs, wantHashPairs) {
109+
t.Errorf("HashPairs got %+v want %+v", got.HashPairs, wantHashPairs)
110+
}
111+
112+
wantResidues := []StringAndSlice{{S: "residue", Slice: []string{"foo"}}}
113+
if !reflect.DeepEqual(got.Residues, wantResidues) {
114+
t.Errorf("Residues got %v want %v", got.Residues, wantResidues)
115+
}
116+
}
117+
118+
// If the compare key is missing, then there is nothing to compare.
119+
func TestMissingCompare(t *testing.T) {
120+
results := []*benchfmt.Result{
121+
{
122+
Config: []benchfmt.Config{
123+
makeConfig("runstamp", "2020-01-01T00:00:00Z"),
124+
makeConfig("numerator-hash", "abcdef0123456789"),
125+
makeConfig("denominator-hash", "9876543219fedcba"),
126+
makeConfig("numerator-hash-time", "2020-02-02T00:00:00Z"),
127+
makeConfig("residue", "foo"),
128+
},
129+
Name: []byte("Foo"),
130+
Iters: 10,
131+
Values: []benchfmt.Value{
132+
{Value: 1, Unit: "sec"},
133+
},
134+
},
135+
{
136+
Config: []benchfmt.Config{
137+
makeConfig("runstamp", "2020-01-01T00:00:00Z"),
138+
makeConfig("numerator-hash", "abcdef0123456789"),
139+
makeConfig("denominator-hash", "9876543219fedcba"),
140+
makeConfig("numerator-hash-time", "2020-02-02T00:00:00Z"),
141+
makeConfig("residue", "foo"),
142+
},
143+
Name: []byte("Foo"),
144+
Iters: 10,
145+
Values: []benchfmt.Value{
146+
{Value: 10, Unit: "sec"},
147+
},
148+
},
149+
}
150+
151+
opts := &BuilderOptions{
152+
Filter: ".unit:/.*/",
153+
Series: "numerator-hash-time",
154+
Table: "", // .unit only
155+
Experiment: "runstamp",
156+
Compare: "compare",
157+
Numerator: "numerator",
158+
Denominator: "denominator",
159+
NumeratorHash: "numerator-hash",
160+
DenominatorHash: "denominator-hash",
161+
Warn: func(format string, args ...interface{}) {
162+
s := fmt.Sprintf(format, args...)
163+
t.Errorf("benchseries warning: %s", s)
164+
},
165+
}
166+
builder, err := NewBuilder(opts)
167+
if err != nil {
168+
t.Fatalf("NewBuilder get err %v, want err nil", err)
169+
}
170+
171+
for _, r := range results {
172+
builder.Add(r)
173+
}
174+
175+
comparisons, err := builder.AllComparisonSeries(nil, DUPE_REPLACE)
176+
if err != nil {
177+
t.Fatalf("AllComparisonSeries got err %v want err nil", err)
178+
}
179+
180+
if len(comparisons) != 1 {
181+
t.Fatalf("len(comparisons) got %d want 1; comparisons: %+v", len(comparisons), comparisons)
182+
}
183+
184+
got := comparisons[0]
185+
186+
if len(got.Series) != 0 {
187+
t.Errorf("len(Series) got %d want 0; Series: %+v", len(got.Series), got.Series)
188+
}
189+
}

0 commit comments

Comments
 (0)