Skip to content

Commit 31e5d83

Browse files
committed
testing: probe with N=1
Change control flow to probe with N=1. This calls benchFunc the same number of times as the old implementation in the absence of subbenchmarks. To be compatible with existing tools, benchmarking only prints a line for "leaf" benchmarks. This means, though, that the name of a benchmark can only be printed after the first iteration. Issue #14863 Change-Id: Ic7b9b89b058f8ebb5287755f24f9e47df8c9537c Reviewed-on: https://go-review.googlesource.com/21043 Run-TryBot: Marcel van Lohuizen <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 2e79d7f commit 31e5d83

File tree

2 files changed

+26
-26
lines changed

2 files changed

+26
-26
lines changed

src/testing/benchmark.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ func roundUp(n int) int {
189189
}
190190
}
191191

192-
// probe runs benchFunc to examine if it has any subbenchmarks.
193-
func (b *B) probe() {
192+
// run1 runs the first iteration of benchFunc. It returns whether more
193+
// iterations of this benchmarks should be run.
194+
func (b *B) run1() bool {
194195
if ctx := b.context; ctx != nil {
195196
// Extend maxLen, if needed.
196197
if n := len(b.name) + ctx.extLen + 1; n > ctx.maxLen {
@@ -204,17 +205,14 @@ func (b *B) probe() {
204205
b.signal <- true
205206
}()
206207

207-
benchmarkLock.Lock()
208-
defer benchmarkLock.Unlock()
209-
210-
b.N = 0
211-
b.benchFunc(b)
208+
b.runN(1)
212209
}()
213210
<-b.signal
211+
return !b.hasSub
214212
}
215213

216214
// run executes the benchmark in a separate goroutine, including all of its
217-
// subbenchmarks.
215+
// subbenchmarks. b must not have subbenchmarks.
218216
func (b *B) run() BenchmarkResult {
219217
if b.context != nil {
220218
// Running go test --test.bench
@@ -235,20 +233,17 @@ func (b *B) doBench() BenchmarkResult {
235233
// launch launches the benchmark function. It gradually increases the number
236234
// of benchmark iterations until the benchmark runs for the requested benchtime.
237235
// launch is run by the doBench function as a separate goroutine.
236+
// run1 must have been called on b.
238237
func (b *B) launch() {
239-
// Run the benchmark for a single iteration in case it's expensive.
240-
n := 1
241-
242238
// Signal that we're done whether we return normally
243239
// or by FailNow's runtime.Goexit.
244240
defer func() {
245241
b.signal <- true
246242
}()
247243

248-
b.runN(n)
249244
// Run the benchmark for at least the specified amount of time.
250245
d := b.benchTime
251-
for !b.failed && b.duration < d && n < 1e9 {
246+
for n := 1; !b.failed && b.duration < d && n < 1e9; {
252247
last := n
253248
// Predict required iterations.
254249
if b.nsPerOp() == 0 {
@@ -392,18 +387,22 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc
392387

393388
// processBench runs bench b for the configured CPU counts and prints the results.
394389
func (ctx *benchContext) processBench(b *B) {
395-
for _, procs := range cpuList {
390+
for i, procs := range cpuList {
396391
runtime.GOMAXPROCS(procs)
397392
benchName := benchmarkName(b.name, procs)
398-
b := &B{
399-
common: common{
400-
signal: make(chan bool),
401-
name: benchName,
402-
},
403-
benchFunc: b.benchFunc,
404-
benchTime: b.benchTime,
405-
}
406393
fmt.Printf("%-*s\t", ctx.maxLen, benchName)
394+
// Recompute the running time for all but the first iteration.
395+
if i > 0 {
396+
b = &B{
397+
common: common{
398+
signal: make(chan bool),
399+
name: benchName,
400+
},
401+
benchFunc: b.benchFunc,
402+
benchTime: b.benchTime,
403+
}
404+
b.run1()
405+
}
407406
r := b.doBench()
408407
if b.failed {
409408
// The output could be very long here, but probably isn't.
@@ -433,7 +432,7 @@ func (ctx *benchContext) processBench(b *B) {
433432
// whether there were any failures.
434433
//
435434
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
436-
// least once will not be measured itself.
435+
// least once will not be measured itself and will be called once with N=1.
437436
func (b *B) Run(name string, f func(b *B)) bool {
438437
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
439438
// Release the lock and acquire it on exit to ensure locks stay paired.
@@ -459,9 +458,10 @@ func (b *B) Run(name string, f func(b *B)) bool {
459458
benchTime: b.benchTime,
460459
context: b.context,
461460
}
462-
if sub.probe(); !sub.hasSub {
463-
b.add(sub.run())
461+
if sub.run1() {
462+
sub.run()
464463
}
464+
b.add(sub.result)
465465
return !sub.failed
466466
}
467467

src/testing/sub_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ func TestBRun(t *T) {
403403
benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure.
404404
benchTime: time.Microsecond,
405405
}
406-
root.run()
406+
root.runN(1)
407407
if ok != !tc.failed {
408408
t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, !tc.failed)
409409
}

0 commit comments

Comments
 (0)