Skip to content

Commit 2f849c1

Browse files
committed
wip: explore accumulated markers
1 parent b3beed3 commit 2f849c1

File tree

3 files changed

+111
-10
lines changed

3 files changed

+111
-10
lines changed

example/helper.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package example
2+
3+
import "time"
4+
5+
//go:noinline
6+
func recursiveFib(n int) uint64 {
7+
if n <= 1 {
8+
return uint64(n)
9+
}
10+
return recursiveFib(n-1) + recursiveFib(n-2)
11+
}
12+
13+
//go:noinline
14+
func expensiveOperation() uint64 {
15+
// Large memory allocation
16+
data := make([]uint64, 1024*1024) // 8 MiB allocation
17+
for i := range data {
18+
data[i] = 42
19+
}
20+
21+
// Expensive recursive computation that will dominate flamegraph
22+
fibResult := recursiveFib(30)
23+
24+
// More expensive work - sum the data
25+
sum := uint64(0)
26+
for _, v := range data {
27+
sum += v
28+
}
29+
30+
return sum + fibResult
31+
}
32+
33+
func actualWork() uint64 {
34+
time.Sleep(1 * time.Millisecond)
35+
return 42
36+
}

example/timing_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package example
2+
3+
import (
4+
"runtime"
5+
"testing"
6+
)
7+
8+
func BenchmarkLargeSetup(b *testing.B) {
9+
res := expensiveOperation()
10+
b.ResetTimer()
11+
12+
for i := 0; i < b.N; i++ {
13+
actualWork()
14+
}
15+
16+
runtime.KeepAlive(res)
17+
}
18+
19+
func BenchmarkLargeSetupInLoop(b *testing.B) {
20+
for i := 0; i < b.N; i++ {
21+
b.StopTimer()
22+
expensiveOperation()
23+
b.StartTimer()
24+
25+
actualWork()
26+
}
27+
}

testing/testing/benchmark.go

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ type codspeed struct {
9191
codspeedTimePerRoundNs []time.Duration
9292
codspeedItersPerRound []int64
9393
startTimestamp uint64
94+
95+
// Accumulated timestamp pairs for batch submission
96+
accumulatedStartTimestamps []uint64
97+
accumulatedEndTimestamps []uint64
9498
}
9599

96100
// B is a type passed to [Benchmark] functions to manage benchmark
@@ -176,14 +180,17 @@ func (b *B) StopTimer() {
176180
// b.loop.i |= loopPoisonTimer
177181

178182
endTimestamp := capi.CurrentTimestamp()
179-
b.codspeed.instrument_hooks.AddBenchmarkTimestamps(b.codspeed.startTimestamp, endTimestamp)
183+
184+
// Accumulate timestamps for batch submission instead of immediate hook call
185+
b.codspeed.accumulatedStartTimestamps = append(b.codspeed.accumulatedStartTimestamps, b.codspeed.startTimestamp)
186+
b.codspeed.accumulatedEndTimestamps = append(b.codspeed.accumulatedEndTimestamps, endTimestamp)
180187

181188
// For b.N loops: This will be called in runN which sets b.N to the number of iterations.
182189
// For b.Loop() loops: loopSlowPath sets b.N to 0 to prevent b.N loops within b.Loop. However, since
183190
// we're starting/stopping the timer for each iteration in the b.Loop() loop, we can use 1 as
184191
// the number of iterations for this round.
185-
b.codspeedItersPerRound = append(b.codspeedItersPerRound, max(int64(b.N), 1))
186-
b.codspeedTimePerRoundNs = append(b.codspeedTimePerRoundNs, highPrecisionTimeSince(b.start))
192+
b.codspeed.codspeedItersPerRound = append(b.codspeed.codspeedItersPerRound, max(int64(b.N), 1))
193+
b.codspeed.codspeedTimePerRoundNs = append(b.codspeed.codspeedTimePerRoundNs, highPrecisionTimeSince(b.start))
187194
}
188195
}
189196

@@ -207,6 +214,21 @@ func (b *B) ResetTimer() {
207214
b.duration = 0
208215
b.netAllocs = 0
209216
b.netBytes = 0
217+
218+
// Clear accumulated CodSpeed timestamp data
219+
b.codspeed.codspeedItersPerRound = b.codspeed.codspeedItersPerRound[:0]
220+
b.codspeed.codspeedTimePerRoundNs = b.codspeed.codspeedTimePerRoundNs[:0]
221+
b.codspeed.accumulatedStartTimestamps = b.codspeed.accumulatedStartTimestamps[:0]
222+
b.codspeed.accumulatedEndTimestamps = b.codspeed.accumulatedEndTimestamps[:0]
223+
}
224+
225+
func (b *B) submitAccumulatedTimestamps() {
226+
for i := 0; i < len(b.codspeed.accumulatedStartTimestamps); i++ {
227+
b.codspeed.instrument_hooks.AddBenchmarkTimestamps(
228+
b.codspeed.accumulatedStartTimestamps[i],
229+
b.codspeed.accumulatedEndTimestamps[i],
230+
)
231+
}
210232
}
211233

212234
// SetBytes records the number of bytes processed in a single operation.
@@ -391,8 +413,10 @@ func (b *B) launch() {
391413
}
392414

393415
// Reset the fields from the warmup run
394-
b.codspeedItersPerRound = make([]int64, 0)
395-
b.codspeedTimePerRoundNs = make([]time.Duration, 0)
416+
b.codspeed.codspeedItersPerRound = make([]int64, 0)
417+
b.codspeed.codspeedTimePerRoundNs = make([]time.Duration, 0)
418+
b.codspeed.accumulatedStartTimestamps = make([]uint64, 0)
419+
b.codspeed.accumulatedEndTimestamps = make([]uint64, 0)
396420

397421
// Final run:
398422
benchD := time.Second * b.benchTime.d
@@ -419,7 +443,11 @@ func (b *B) launch() {
419443
b.codspeed.instrument_hooks.StopBenchmark()
420444
}
421445
}
422-
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.codspeedTimePerRoundNs, b.codspeedItersPerRound, b.extra}
446+
447+
// Submit all accumulated timestamps to CodSpeed hooks before finalizing results
448+
b.submitAccumulatedTimestamps()
449+
450+
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.codspeed.codspeedTimePerRoundNs, b.codspeed.codspeedItersPerRound, b.extra}
423451
}
424452

425453
// Elapsed returns the measured elapsed time of the benchmark.
@@ -458,6 +486,10 @@ func (b *B) stopOrScaleBLoop() bool {
458486
// Stop the timer so we don't count cleanup time
459487
b.StopTimer()
460488
b.codspeed.instrument_hooks.StopBenchmark()
489+
490+
// Submit all accumulated timestamps to CodSpeed hooks before benchmark completion
491+
b.submitAccumulatedTimestamps()
492+
461493
// Commit iteration count
462494
b.N = int(b.loop.n)
463495
b.loop.done = true
@@ -496,8 +528,10 @@ func (b *B) loopSlowPath() bool {
496528
b.N = 0
497529
b.loop.i++
498530

499-
b.codspeedItersPerRound = make([]int64, 0)
500-
b.codspeedTimePerRoundNs = make([]time.Duration, 0)
531+
b.codspeed.codspeedItersPerRound = make([]int64, 0)
532+
b.codspeed.codspeedTimePerRoundNs = make([]time.Duration, 0)
533+
b.codspeed.accumulatedStartTimestamps = make([]uint64, 0)
534+
b.codspeed.accumulatedEndTimestamps = make([]uint64, 0)
501535

502536
b.codspeed.instrument_hooks.StartBenchmark()
503537
b.ResetTimer()
@@ -515,6 +549,10 @@ func (b *B) loopSlowPath() bool {
515549
}
516550
b.StopTimer()
517551
b.codspeed.instrument_hooks.StopBenchmark()
552+
553+
// Submit all accumulated timestamps to CodSpeed hooks before benchmark completion
554+
b.submitAccumulatedTimestamps()
555+
518556
// Commit iteration count
519557
b.N = int(b.loop.n)
520558
b.loop.done = true
@@ -900,8 +938,8 @@ func (s *benchState) processBench(b *B) {
900938
Name: benchName,
901939
Uri: benchUri,
902940
Pid: os.Getpid(),
903-
CodspeedTimePerRoundNs: r.CodspeedTimePerRoundNs,
904-
CodspeedItersPerRound: r.CodspeedItersPerRound,
941+
CodspeedTimePerRoundNs: r.codspeedTimePerRoundNs,
942+
CodspeedItersPerRound: r.codspeedItersPerRound,
905943
}
906944

907945
goRunnerMetadata, err := findGoRunnerMetadata()

0 commit comments

Comments
 (0)