@@ -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
@@ -165,19 +169,20 @@ func (b *B) StartTimer() {
165
169
// while performing steps that you don't want to measure.
166
170
func (b * B ) StopTimer () {
167
171
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 ))
174
172
b .duration += highPrecisionTimeSince (b .start )
175
173
// runtime.ReadMemStats(&memStats)
176
174
// b.netAllocs += memStats.Mallocs - b.startAllocs
177
175
// b.netBytes += memStats.TotalAlloc - b.startBytes
178
176
b .timerOn = false
179
177
// If we hit B.Loop with the timer stopped, fail.
180
178
// b.loop.i |= loopPoisonTimer
179
+
180
+ // For b.N loops: This will be called in runN which sets b.N to the number of iterations.
181
+ // For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
182
+ // we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
183
+ // the number of iterations for this round.
184
+ b .codspeedItersPerRound = append (b .codspeedItersPerRound , max (int64 (b .N ), 1 ))
185
+ b .codspeedTimePerRoundNs = append (b .codspeedTimePerRoundNs , highPrecisionTimeSince (b .start ))
181
186
}
182
187
}
183
188
@@ -197,10 +202,29 @@ func (b *B) ResetTimer() {
197
202
b .startAllocs = memStats .Mallocs
198
203
b .startBytes = memStats .TotalAlloc
199
204
b .start = highPrecisionTimeNow ()
205
+
206
+ b .startTimestamp = capi .CurrentTimestamp ()
200
207
}
201
208
b .duration = 0
202
209
b .netAllocs = 0
203
210
b .netBytes = 0
211
+
212
+ // Clear CodSpeed timestamp data
213
+ b .codspeedItersPerRound = b .codspeedItersPerRound [:0 ]
214
+ b .codspeedTimePerRoundNs = b .codspeedTimePerRoundNs [:0 ]
215
+ b .startTimestamps = b .startTimestamps [:0 ]
216
+ b .stopTimestamps = b .stopTimestamps [:0 ]
217
+ }
218
+
219
+ func (b * B ) sendAccumulatedTimestamps () {
220
+ for i := 0 ; i < len (b .startTimestamps ); i ++ {
221
+ b .instrument_hooks .AddBenchmarkTimestamps (
222
+ b .startTimestamps [i ],
223
+ b .stopTimestamps [i ],
224
+ )
225
+ }
226
+ b .startTimestamps = b .startTimestamps [:0 ]
227
+ b .stopTimestamps = b .stopTimestamps [:0 ]
204
228
}
205
229
206
230
// SetBytes records the number of bytes processed in a single operation.
@@ -242,11 +266,23 @@ func (b *B) __codspeed_root_frame__runN(n int) {
242
266
b .parallelism = 1
243
267
b .ResetTimer ()
244
268
b .StartTimer ()
269
+ startTimestamp := capi .CurrentTimestamp ()
245
270
b .benchFunc (b )
271
+ endTimestamp = capi .CurrentTimestamp ()
246
272
b .StopTimer ()
247
273
b .previousN = n
248
274
b .previousDuration = b .duration
249
275
276
+ if b .startTimestamp >= endTimestamp {
277
+ // This should never happen, unless we have a bug in the timer logic.
278
+ panic (fmt .Sprintf ("Invalid benchmark timestamps: start timestamp (%d) is greater than or equal to end timestamp (%d)" , b .startTimestamp , endTimestamp ))
279
+ }
280
+ b .startTimestamps = append (b .startTimestamps , b .startTimestamp )
281
+ b .stopTimestamps = append (b .stopTimestamps , endTimestamp )
282
+
283
+ // Reset to prevent accidental reuse
284
+ b .startTimestamp = 0
285
+
250
286
if b .loop .n > 0 && ! b .loop .done && ! b .failed {
251
287
b .Error ("benchmark function returned without B.Loop() == false (break or return in loop?)" )
252
288
}
@@ -385,8 +421,7 @@ func (b *B) launch() {
385
421
}
386
422
387
423
// Reset the fields from the warmup run
388
- b .codspeedItersPerRound = make ([]int64 , 0 )
389
- b .codspeedTimePerRoundNs = make ([]time.Duration , 0 )
424
+ b .ResetTimer ()
390
425
391
426
// Final run:
392
427
benchD := b .benchTime .d
@@ -411,6 +446,7 @@ func (b *B) launch() {
411
446
b .runN (int (roundN ))
412
447
}
413
448
b .codspeed .instrument_hooks .StopBenchmark ()
449
+ b .sendAccumulatedTimestamps ()
414
450
}
415
451
}
416
452
b .result = BenchmarkResult {b .N , b .duration , b .bytes , b .netAllocs , b .netBytes , b .codspeedTimePerRoundNs , b .codspeedItersPerRound , b .extra }
@@ -452,6 +488,8 @@ func (b *B) stopOrScaleBLoop() bool {
452
488
// Stop the timer so we don't count cleanup time
453
489
b .StopTimer ()
454
490
b .codspeed .instrument_hooks .StopBenchmark ()
491
+ b .sendAccumulatedTimestamps ()
492
+
455
493
// Commit iteration count
456
494
b .N = int (b .loop .n )
457
495
b .loop .done = true
@@ -490,9 +528,6 @@ func (b *B) loopSlowPath() bool {
490
528
b .N = 0
491
529
b .loop .i ++
492
530
493
- b .codspeedItersPerRound = make ([]int64 , 0 )
494
- b .codspeedTimePerRoundNs = make ([]time.Duration , 0 )
495
-
496
531
b .codspeed .instrument_hooks .StartBenchmark ()
497
532
b .ResetTimer ()
498
533
b .StartTimer ()
@@ -508,6 +543,8 @@ func (b *B) loopSlowPath() bool {
508
543
}
509
544
b .StopTimer ()
510
545
b .codspeed .instrument_hooks .StopBenchmark ()
546
+ b .sendAccumulatedTimestamps ()
547
+
511
548
// Commit iteration count
512
549
b .N = int (b .loop .n )
513
550
b .loop .done = true
0 commit comments