Skip to content

Commit 416dc8a

Browse files
committed
feat(testing): add benchmark markers
1 parent e674755 commit 416dc8a

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

testing/capi/instrument-hooks.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@ typedef struct instruments_root_InstrumentHooks__547 InstrumentHooks;
77
*/
88
import "C"
99
import (
10+
"os"
1011
"runtime"
1112
"unsafe"
1213
)
1314

15+
const (
16+
MarkerTypeSampleStart = 0
17+
MarkerTypeSampleEnd = 1
18+
MarkerTypeBenchmarkStart = 2
19+
MarkerTypeBenchmarkEnd = 3
20+
)
21+
1422
// This will be set in the go-runner
1523
var integrationVersion = "dev"
1624

@@ -79,3 +87,16 @@ func (i *InstrumentHooks) IsInstrumented() bool {
7987
}
8088
return bool(C.instrument_hooks_is_instrumented(i.hooks))
8189
}
90+
91+
func CurrentTimestamp() uint64 {
92+
return uint64(C.instrument_hooks_current_timestamp())
93+
}
94+
95+
func (i *InstrumentHooks) AddBenchmarkTimestamps(startTimestamp, endTimestamp uint64) {
96+
if i.hooks == nil {
97+
return
98+
}
99+
pid := uint32(os.Getpid())
100+
C.instrument_hooks_add_marker(i.hooks, C.uint32_t(pid), C.uint8_t(MarkerTypeBenchmarkStart), C.uint64_t(startTimestamp))
101+
C.instrument_hooks_add_marker(i.hooks, C.uint32_t(pid), C.uint8_t(MarkerTypeBenchmarkEnd), C.uint64_t(endTimestamp))
102+
}

testing/testing/benchmark.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ type codspeed struct {
9090

9191
codspeedTimePerRoundNs []time.Duration
9292
codspeedItersPerRound []int64
93+
94+
startTimestamp uint64
95+
startTimestamps []uint64
96+
stopTimestamps []uint64
9397
}
9498

9599
// B is a type passed to [Benchmark] functions to manage benchmark
@@ -165,19 +169,20 @@ func (b *B) StartTimer() {
165169
// while performing steps that you don't want to measure.
166170
func (b *B) StopTimer() {
167171
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))
174172
b.duration += highPrecisionTimeSince(b.start)
175173
// runtime.ReadMemStats(&memStats)
176174
// b.netAllocs += memStats.Mallocs - b.startAllocs
177175
// b.netBytes += memStats.TotalAlloc - b.startBytes
178176
b.timerOn = false
179177
// If we hit B.Loop with the timer stopped, fail.
180178
// 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))
181186
}
182187
}
183188

@@ -197,10 +202,29 @@ func (b *B) ResetTimer() {
197202
b.startAllocs = memStats.Mallocs
198203
b.startBytes = memStats.TotalAlloc
199204
b.start = highPrecisionTimeNow()
205+
206+
b.startTimestamp = capi.CurrentTimestamp()
200207
}
201208
b.duration = 0
202209
b.netAllocs = 0
203210
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]
204228
}
205229

206230
// SetBytes records the number of bytes processed in a single operation.
@@ -242,11 +266,23 @@ func (b *B) __codspeed_root_frame__runN(n int) {
242266
b.parallelism = 1
243267
b.ResetTimer()
244268
b.StartTimer()
269+
startTimestamp := capi.CurrentTimestamp()
245270
b.benchFunc(b)
271+
endTimestamp = capi.CurrentTimestamp()
246272
b.StopTimer()
247273
b.previousN = n
248274
b.previousDuration = b.duration
249275

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+
250286
if b.loop.n > 0 && !b.loop.done && !b.failed {
251287
b.Error("benchmark function returned without B.Loop() == false (break or return in loop?)")
252288
}
@@ -385,8 +421,7 @@ func (b *B) launch() {
385421
}
386422

387423
// Reset the fields from the warmup run
388-
b.codspeedItersPerRound = make([]int64, 0)
389-
b.codspeedTimePerRoundNs = make([]time.Duration, 0)
424+
b.ResetTimer()
390425

391426
// Final run:
392427
benchD := b.benchTime.d
@@ -411,6 +446,7 @@ func (b *B) launch() {
411446
b.runN(int(roundN))
412447
}
413448
b.codspeed.instrument_hooks.StopBenchmark()
449+
b.sendAccumulatedTimestamps()
414450
}
415451
}
416452
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 {
452488
// Stop the timer so we don't count cleanup time
453489
b.StopTimer()
454490
b.codspeed.instrument_hooks.StopBenchmark()
491+
b.sendAccumulatedTimestamps()
492+
455493
// Commit iteration count
456494
b.N = int(b.loop.n)
457495
b.loop.done = true
@@ -490,9 +528,6 @@ func (b *B) loopSlowPath() bool {
490528
b.N = 0
491529
b.loop.i++
492530

493-
b.codspeedItersPerRound = make([]int64, 0)
494-
b.codspeedTimePerRoundNs = make([]time.Duration, 0)
495-
496531
b.codspeed.instrument_hooks.StartBenchmark()
497532
b.ResetTimer()
498533
b.StartTimer()
@@ -508,6 +543,8 @@ func (b *B) loopSlowPath() bool {
508543
}
509544
b.StopTimer()
510545
b.codspeed.instrument_hooks.StopBenchmark()
546+
b.sendAccumulatedTimestamps()
547+
511548
// Commit iteration count
512549
b.N = int(b.loop.n)
513550
b.loop.done = true

0 commit comments

Comments
 (0)