@@ -90,6 +90,10 @@ type codspeed struct {
90
90
91
91
codspeedTimePerRoundNs []time.Duration
92
92
codspeedItersPerRound []int64
93
+
94
+ startTimestamp uint64
95
+ startTimestamps []uint64
96
+ stopTimestamps []uint64
93
97
}
94
98
95
99
// B is a type passed to [Benchmark] functions to manage benchmark
@@ -158,26 +162,40 @@ func (b *B) StartTimer() {
158
162
b .start = highPrecisionTimeNow ()
159
163
b .timerOn = true
160
164
// b.loop.i &^= loopPoisonTimer
165
+ b .startTimestamp = capi .CurrentTimestamp ()
161
166
}
162
167
}
163
168
164
169
// StopTimer stops timing a test. This can be used to pause the timer
165
170
// while performing steps that you don't want to measure.
166
171
func (b * B ) StopTimer () {
167
172
if b .timerOn {
168
- // For b.N loops: This will be called in runN which sets b.N to the number of iterations.
169
- // For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
170
- // we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
171
- // the number of iterations for this round.
172
- b .codspeedItersPerRound = append (b .codspeedItersPerRound , max (int64 (b .N ), 1 ))
173
- b .codspeedTimePerRoundNs = append (b .codspeedTimePerRoundNs , highPrecisionTimeSince (b .start ))
173
+ endTimestamp := capi .CurrentTimestamp ()
174
174
b .duration += highPrecisionTimeSince (b .start )
175
175
// runtime.ReadMemStats(&memStats)
176
176
// b.netAllocs += memStats.Mallocs - b.startAllocs
177
177
// b.netBytes += memStats.TotalAlloc - b.startBytes
178
178
b .timerOn = false
179
179
// If we hit B.Loop with the timer stopped, fail.
180
180
// b.loop.i |= loopPoisonTimer
181
+
182
+ if b .startTimestamp >= endTimestamp {
183
+ // This should never happen, unless we have a bug in the
184
+ // timer logic.
185
+ panic (fmt .Sprintf ("Invalid benchmark timestamps: start timestamp (%d) is greater than or equal to end timestamp (%d)" , b .startTimestamp , endTimestamp ))
186
+ }
187
+ b .startTimestamps = append (b .startTimestamps , b .startTimestamp )
188
+ b .stopTimestamps = append (b .stopTimestamps , endTimestamp )
189
+
190
+ // Reset to prevent accidental reuse
191
+ b .startTimestamp = 0
192
+
193
+ // For b.N loops: This will be called in runN which sets b.N to the number of iterations.
194
+ // For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
195
+ // we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
196
+ // the number of iterations for this round.
197
+ b .codspeedItersPerRound = append (b .codspeedItersPerRound , max (int64 (b .N ), 1 ))
198
+ b .codspeedTimePerRoundNs = append (b .codspeedTimePerRoundNs , highPrecisionTimeSince (b .start ))
181
199
}
182
200
}
183
201
@@ -197,10 +215,29 @@ func (b *B) ResetTimer() {
197
215
b .startAllocs = memStats .Mallocs
198
216
b .startBytes = memStats .TotalAlloc
199
217
b .start = highPrecisionTimeNow ()
218
+
219
+ b .startTimestamp = capi .CurrentTimestamp ()
200
220
}
201
221
b .duration = 0
202
222
b .netAllocs = 0
203
223
b .netBytes = 0
224
+
225
+ // Clear CodSpeed timestamp data
226
+ b .codspeedItersPerRound = b .codspeedItersPerRound [:0 ]
227
+ b .codspeedTimePerRoundNs = b .codspeedTimePerRoundNs [:0 ]
228
+ b .startTimestamps = b .startTimestamps [:0 ]
229
+ b .stopTimestamps = b .stopTimestamps [:0 ]
230
+ }
231
+
232
+ func (b * B ) sendAccumulatedTimestamps () {
233
+ for i := 0 ; i < len (b .startTimestamps ); i ++ {
234
+ b .instrument_hooks .AddBenchmarkTimestamps (
235
+ b .startTimestamps [i ],
236
+ b .stopTimestamps [i ],
237
+ )
238
+ }
239
+ b .startTimestamps = b .startTimestamps [:0 ]
240
+ b .stopTimestamps = b .stopTimestamps [:0 ]
204
241
}
205
242
206
243
// SetBytes records the number of bytes processed in a single operation.
@@ -385,8 +422,7 @@ func (b *B) launch() {
385
422
}
386
423
387
424
// Reset the fields from the warmup run
388
- b .codspeedItersPerRound = make ([]int64 , 0 )
389
- b .codspeedTimePerRoundNs = make ([]time.Duration , 0 )
425
+ b .ResetTimer ()
390
426
391
427
// Final run:
392
428
benchD := b .benchTime .d
@@ -411,6 +447,7 @@ func (b *B) launch() {
411
447
b .runN (int (roundN ))
412
448
}
413
449
b .codspeed .instrument_hooks .StopBenchmark ()
450
+ b .sendAccumulatedTimestamps ()
414
451
}
415
452
}
416
453
b .result = BenchmarkResult {b .N , b .duration , b .bytes , b .netAllocs , b .netBytes , b .codspeedTimePerRoundNs , b .codspeedItersPerRound , b .extra }
@@ -452,6 +489,8 @@ func (b *B) stopOrScaleBLoop() bool {
452
489
// Stop the timer so we don't count cleanup time
453
490
b .StopTimer ()
454
491
b .codspeed .instrument_hooks .StopBenchmark ()
492
+ b .sendAccumulatedTimestamps ()
493
+
455
494
// Commit iteration count
456
495
b .N = int (b .loop .n )
457
496
b .loop .done = true
@@ -490,9 +529,6 @@ func (b *B) loopSlowPath() bool {
490
529
b .N = 0
491
530
b .loop .i ++
492
531
493
- b .codspeedItersPerRound = make ([]int64 , 0 )
494
- b .codspeedTimePerRoundNs = make ([]time.Duration , 0 )
495
-
496
532
b .codspeed .instrument_hooks .StartBenchmark ()
497
533
b .ResetTimer ()
498
534
b .StartTimer ()
@@ -509,6 +545,8 @@ func (b *B) loopSlowPath() bool {
509
545
}
510
546
b .StopTimer ()
511
547
b .codspeed .instrument_hooks .StopBenchmark ()
548
+ b .sendAccumulatedTimestamps ()
549
+
512
550
// Commit iteration count
513
551
b .N = int (b .loop .n )
514
552
b .loop .done = true
0 commit comments