Skip to content

Commit d328bb0

Browse files
committed
fix(testing): save measurements only in the benchmark framework
1 parent fbe2077 commit d328bb0

File tree

3 files changed

+90
-6
lines changed

3 files changed

+90
-6
lines changed

testing/fork.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ find . -type f -name "*.go" -not -name "*_test.go" -exec sed -i 's|"testing"|tes
122122
restore_files "${CODSPEED_FILES[@]}"
123123

124124
apply_patch "patches/benchmark_stopbenchmark_fail.patch" 10 ".."
125+
apply_patch "patches/benchmark_stoptimer_mitigation.patch" 10 ".."
125126

126127

127128
# Run pre-commit and format the code
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
diff --git a/testing/testing/benchmark.go b/testing/testing/benchmark.go
2+
index 8fdea90..9473443 100644
3+
--- a/testing/testing/benchmark.go
4+
+++ b/testing/testing/benchmark.go
5+
@@ -93,6 +93,10 @@ type codspeed struct {
6+
startTimestamp uint64
7+
startTimestamps []uint64
8+
stopTimestamps []uint64
9+
+
10+
+ // Indicates whether a measurement has been saved already. This aims to prevent saving measurements
11+
+ // twice, because `b.Loop()` saves them internally as well but is also called from runN
12+
+ savedMeasurement bool
13+
}
14+
15+
// B is a type passed to [Benchmark] functions to manage benchmark
16+
@@ -160,6 +164,7 @@ func (b *B) StartTimerWithoutMarker() {
17+
// b.startBytes = memStats.TotalAlloc
18+
b.start = highPrecisionTimeNow()
19+
b.timerOn = true
20+
+ b.savedMeasurement = false
21+
// b.loop.i &^= loopPoisonTimer
22+
}
23+
}
24+
@@ -186,14 +191,22 @@ func (b *B) StopTimerWithoutMarker() {
25+
b.timerOn = false
26+
// If we hit B.Loop with the timer stopped, fail.
27+
// b.loop.i |= loopPoisonTimer
28+
+ }
29+
+}
30+
31+
- // For b.N loops: This will be called in runN which sets b.N to the number of iterations.
32+
- // For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
33+
- // we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
34+
- // the number of iterations for this round.
35+
- b.codspeedItersPerRound = append(b.codspeedItersPerRound, max(int64(b.N), 1))
36+
- b.codspeedTimePerRoundNs = append(b.codspeedTimePerRoundNs, timeSinceStart)
37+
+func (b *B) SaveMeasurement() {
38+
+ if b.savedMeasurement {
39+
+ return
40+
}
41+
+ b.savedMeasurement = true
42+
+
43+
+ // For b.N loops: This will be called in runN which sets b.N to the number of iterations.
44+
+ // For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
45+
+ // we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
46+
+ // the number of iterations for this round.
47+
+ timeSinceStart := highPrecisionTimeSince(b.start)
48+
+ b.codspeedItersPerRound = append(b.codspeedItersPerRound, max(int64(b.N), 1))
49+
+ b.codspeedTimePerRoundNs = append(b.codspeedTimePerRoundNs, timeSinceStart)
50+
}
51+
52+
func (b *B) StopTimer() {
53+
@@ -296,6 +309,7 @@ func (b *B) __codspeed_root_frame__runN(n int) {
54+
b.ResetTimer()
55+
b.StartTimer()
56+
b.benchFunc(b)
57+
+ b.SaveMeasurement()
58+
b.StopTimer()
59+
b.previousN = n
60+
b.previousDuration = b.duration
61+
@@ -614,6 +628,7 @@ func (b *B) loopSlowPath() bool {
62+
// whereas b.N-based benchmarks must run the benchmark function (and any
63+
// associated setup and cleanup) several times.
64+
func (b *B) Loop() bool {
65+
+ b.SaveMeasurement()
66+
b.StopTimerWithoutMarker()
67+
// This is written such that the fast path is as fast as possible and can be
68+
// inlined.

testing/testing/benchmark.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ type codspeed struct {
9393
startTimestamp uint64
9494
startTimestamps []uint64
9595
stopTimestamps []uint64
96+
97+
// Indicates whether a measurement has been saved already. This aims to prevent saving measurements
98+
// twice, because `b.Loop()` saves them internally as well but is also called from runN
99+
savedMeasurement bool
96100
}
97101

98102
// B is a type passed to [Benchmark] functions to manage benchmark
@@ -160,6 +164,7 @@ func (b *B) StartTimerWithoutMarker() {
160164
// b.startBytes = memStats.TotalAlloc
161165
b.start = highPrecisionTimeNow()
162166
b.timerOn = true
167+
b.savedMeasurement = false
163168
// b.loop.i &^= loopPoisonTimer
164169
}
165170
}
@@ -186,14 +191,22 @@ func (b *B) StopTimerWithoutMarker() {
186191
b.timerOn = false
187192
// If we hit B.Loop with the timer stopped, fail.
188193
// b.loop.i |= loopPoisonTimer
194+
}
195+
}
189196

190-
// For b.N loops: This will be called in runN which sets b.N to the number of iterations.
191-
// For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
192-
// we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
193-
// the number of iterations for this round.
194-
b.codspeedItersPerRound = append(b.codspeedItersPerRound, max(int64(b.N), 1))
195-
b.codspeedTimePerRoundNs = append(b.codspeedTimePerRoundNs, timeSinceStart)
197+
func (b *B) SaveMeasurement() {
198+
if b.savedMeasurement {
199+
return
196200
}
201+
b.savedMeasurement = true
202+
203+
// For b.N loops: This will be called in runN which sets b.N to the number of iterations.
204+
// For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
205+
// we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
206+
// the number of iterations for this round.
207+
timeSinceStart := highPrecisionTimeSince(b.start)
208+
b.codspeedItersPerRound = append(b.codspeedItersPerRound, max(int64(b.N), 1))
209+
b.codspeedTimePerRoundNs = append(b.codspeedTimePerRoundNs, timeSinceStart)
197210
}
198211

199212
func (b *B) StopTimer() {
@@ -296,6 +309,7 @@ func (b *B) __codspeed_root_frame__runN(n int) {
296309
b.ResetTimer()
297310
b.StartTimer()
298311
b.benchFunc(b)
312+
b.SaveMeasurement()
299313
b.StopTimer()
300314
b.previousN = n
301315
b.previousDuration = b.duration
@@ -614,6 +628,7 @@ func (b *B) loopSlowPath() bool {
614628
// whereas b.N-based benchmarks must run the benchmark function (and any
615629
// associated setup and cleanup) several times.
616630
func (b *B) Loop() bool {
631+
b.SaveMeasurement()
617632
b.StopTimerWithoutMarker()
618633
// This is written such that the fast path is as fast as possible and can be
619634
// inlined.

0 commit comments

Comments
 (0)