Skip to content

Commit db30948

Browse files
committed
feat(testing): add benchmark markers
1 parent fc8d996 commit db30948

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-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: 49 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
@@ -158,26 +162,40 @@ func (b *B) StartTimer() {
158162
b.start = highPrecisionTimeNow()
159163
b.timerOn = true
160164
// b.loop.i &^= loopPoisonTimer
165+
b.startTimestamp = capi.CurrentTimestamp()
161166
}
162167
}
163168

164169
// StopTimer stops timing a test. This can be used to pause the timer
165170
// while performing steps that you don't want to measure.
166171
func (b *B) StopTimer() {
167172
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()
174174
b.duration += highPrecisionTimeSince(b.start)
175175
// runtime.ReadMemStats(&memStats)
176176
// b.netAllocs += memStats.Mallocs - b.startAllocs
177177
// b.netBytes += memStats.TotalAlloc - b.startBytes
178178
b.timerOn = false
179179
// If we hit B.Loop with the timer stopped, fail.
180180
// 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))
181199
}
182200
}
183201

@@ -197,10 +215,29 @@ func (b *B) ResetTimer() {
197215
b.startAllocs = memStats.Mallocs
198216
b.startBytes = memStats.TotalAlloc
199217
b.start = highPrecisionTimeNow()
218+
219+
b.startTimestamp = capi.CurrentTimestamp()
200220
}
201221
b.duration = 0
202222
b.netAllocs = 0
203223
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]
204241
}
205242

206243
// SetBytes records the number of bytes processed in a single operation.
@@ -385,8 +422,7 @@ func (b *B) launch() {
385422
}
386423

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

391427
// Final run:
392428
benchD := b.benchTime.d
@@ -411,6 +447,7 @@ func (b *B) launch() {
411447
b.runN(int(roundN))
412448
}
413449
b.codspeed.instrument_hooks.StopBenchmark()
450+
b.sendAccumulatedTimestamps()
414451
}
415452
}
416453
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 {
452489
// Stop the timer so we don't count cleanup time
453490
b.StopTimer()
454491
b.codspeed.instrument_hooks.StopBenchmark()
492+
b.sendAccumulatedTimestamps()
493+
455494
// Commit iteration count
456495
b.N = int(b.loop.n)
457496
b.loop.done = true
@@ -490,9 +529,6 @@ func (b *B) loopSlowPath() bool {
490529
b.N = 0
491530
b.loop.i++
492531

493-
b.codspeedItersPerRound = make([]int64, 0)
494-
b.codspeedTimePerRoundNs = make([]time.Duration, 0)
495-
496532
b.codspeed.instrument_hooks.StartBenchmark()
497533
b.ResetTimer()
498534
b.StartTimer()
@@ -509,6 +545,8 @@ func (b *B) loopSlowPath() bool {
509545
}
510546
b.StopTimer()
511547
b.codspeed.instrument_hooks.StopBenchmark()
548+
b.sendAccumulatedTimestamps()
549+
512550
// Commit iteration count
513551
b.N = int(b.loop.n)
514552
b.loop.done = true

0 commit comments

Comments
 (0)