Skip to content

Commit 345c50c

Browse files
craig[bot]herkolategan
andcommitted
Merge #143915
143915: microbench-ci: per metric retry r=tbg,DarrylWong,golgeek a=herkolategan Previously, - The retry confirmation logic considered all metrics of a benchmark together, which could lead to false positives when different metrics regressed on different retries. - The comparison step recalculated regression status instead of using the already-available marker files. This PR addresses both issues by: - Making retry logic track each metric independently, ensuring the same metric must regress consistently across retries - Simplifying the comparison step to use marker files instead of recalculating the regression status. Epic: None Release note: None Co-authored-by: Herko Lategan <[email protected]>
2 parents e3eb5f0 + daa74ec commit 345c50c

File tree

5 files changed

+276
-203
lines changed

5 files changed

+276
-203
lines changed

pkg/cmd/microbench-ci/benchmark.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ func (b *Benchmark) sanitizedName() string {
5555
return sanitizeRe.ReplaceAllString(strings.TrimPrefix(b.Name, "Benchmark"), "_")
5656
}
5757

58+
func (b *Benchmark) markerName(status Status) string {
59+
return b.sanitizedName() + "." + strings.ToUpper(status.String())
60+
}
61+
5862
func (b *Benchmark) binaryName() string {
5963
return b.sanitizedPackageName()
6064
}

pkg/cmd/microbench-ci/compare.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,6 @@ func (c *CompareResult) status(metricName string) Status {
8686
return status
8787
}
8888

89-
// top returns the top status of all metrics in the comparison.
90-
func (c *CompareResult) top() Status {
91-
topStatus := NoChange
92-
for metric := range c.MetricMap {
93-
status := c.status(metric)
94-
if status > topStatus {
95-
topStatus = status
96-
}
97-
}
98-
return topStatus
99-
}
100-
10189
// compare compares the metrics of a benchmark between two revisions. Only the
10290
// specified last number of lines of the benchmark logs are considered. If lines
10391
// is 0, it considers the entire logs.
@@ -153,12 +141,6 @@ func (b Benchmarks) compareBenchmarks() (CompareResults, error) {
153141
if err != nil {
154142
return nil, err
155143
}
156-
if compareResult.top() != NoChange {
157-
compareResult, err = benchmark.compare(0)
158-
if err != nil {
159-
return nil, err
160-
}
161-
}
162144
compareResults = append(compareResults, compareResult)
163145
}
164146
return compareResults, nil

pkg/cmd/microbench-ci/run.go

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,12 @@ func (b *Benchmark) run() error {
7777
return err
7878
}
7979
}
80-
status := NoChange
80+
81+
status := make(map[string]Status)
82+
for _, metric := range b.Metrics {
83+
status[metric.Name] = NoChange
84+
}
85+
8186
tries := 0
8287
for retries := 0; retries < b.Retries; retries++ {
8388
log.Printf("Running benchmark %q for %d iterations (attempt %d)", b.Name, b.Count, retries+1)
@@ -98,35 +103,59 @@ func (b *Benchmark) run() error {
98103
if err != nil {
99104
return err
100105
}
101-
currentStatus := compareResult.top()
102-
// If the benchmark shows no change, we can stop running additional retry
103-
// iterations. Retries are run once a benchmark has regressed or improved,
104-
// on the first try, to ensure that the change is not a fluke.
105-
if currentStatus == NoChange {
106-
status = currentStatus
107-
break
106+
107+
// Loop through all metrics and check if they have a consistent status.
108+
// As long as one metric maintains a consistent status (other than
109+
// NoChange), we continue running additional retry iterations.
110+
keepRunning := false
111+
for _, metric := range b.Metrics {
112+
currentStatus := compareResult.status(metric.Name)
113+
// If this is the first run, we set the status to the current
114+
// status. Only continue running if a metric has regressed or
115+
// improved.
116+
if retries == 0 {
117+
status[metric.Name] = currentStatus
118+
if currentStatus != NoChange {
119+
keepRunning = true
120+
}
121+
continue
122+
}
123+
124+
// On a retry, if any metric continues having a consistent state (other
125+
// than NoChange), we run additional retries.
126+
if status[metric.Name] != NoChange {
127+
if status[metric.Name] == currentStatus {
128+
keepRunning = true
129+
} else {
130+
// The metric previously indicated a change, but now
131+
// indicates a change in the opposite direction. Reset the
132+
// status to NoChange to indicate that the benchmark did not
133+
// change.
134+
status[metric.Name] = NoChange
135+
}
136+
}
108137
}
109138

110-
// If this is the first run, we set the status to the current status.
111-
// Otherwise, we check if the regression or improvement is persistent and
112-
// continue running until all retries are exhausted. If the status flips
113-
// from a regression to an improvement or vice versa, we set the status to
114-
// NoChange and stop running, as the results are inconclusive.
115-
if status == NoChange {
116-
status = currentStatus
117-
} else if status != currentStatus {
118-
// If the benchmark shows a different change, the results are inconclusive.
119-
status = NoChange
139+
if !keepRunning {
140+
// Reset all metrics to NoChange to indicate that the benchmark did
141+
// not change, and we can stop running additional retries.
142+
for _, metric := range b.Metrics {
143+
status[metric.Name] = NoChange
144+
}
120145
break
121146
}
122147
}
123148

124-
// Write change marker file if the benchmark changed.
125-
if status != NoChange {
126-
marker := strings.ToUpper(status.String())
127-
err := os.WriteFile(path.Join(suite.artifactsDir(New), b.sanitizedName()+"."+marker), nil, 0644)
128-
if err != nil {
129-
return err
149+
// Write change marker file (per metric) if the benchmark changed. It's
150+
// possible that both a regression and an improvement occurred, for
151+
// different metrics of the same benchmark, in which case both markers will
152+
// be written.
153+
for _, metric := range b.Metrics {
154+
if status[metric.Name] != NoChange {
155+
err := os.WriteFile(path.Join(suite.artifactsDir(New), b.markerName(status[metric.Name])), nil, 0644)
156+
if err != nil {
157+
return err
158+
}
130159
}
131160
}
132161

0 commit comments

Comments
 (0)