@@ -189,8 +189,9 @@ func roundUp(n int) int {
189
189
}
190
190
}
191
191
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 {
194
195
if ctx := b .context ; ctx != nil {
195
196
// Extend maxLen, if needed.
196
197
if n := len (b .name ) + ctx .extLen + 1 ; n > ctx .maxLen {
@@ -204,17 +205,14 @@ func (b *B) probe() {
204
205
b .signal <- true
205
206
}()
206
207
207
- benchmarkLock .Lock ()
208
- defer benchmarkLock .Unlock ()
209
-
210
- b .N = 0
211
- b .benchFunc (b )
208
+ b .runN (1 )
212
209
}()
213
210
<- b .signal
211
+ return ! b .hasSub
214
212
}
215
213
216
214
// run executes the benchmark in a separate goroutine, including all of its
217
- // subbenchmarks.
215
+ // subbenchmarks. b must not have subbenchmarks.
218
216
func (b * B ) run () BenchmarkResult {
219
217
if b .context != nil {
220
218
// Running go test --test.bench
@@ -235,20 +233,17 @@ func (b *B) doBench() BenchmarkResult {
235
233
// launch launches the benchmark function. It gradually increases the number
236
234
// of benchmark iterations until the benchmark runs for the requested benchtime.
237
235
// launch is run by the doBench function as a separate goroutine.
236
+ // run1 must have been called on b.
238
237
func (b * B ) launch () {
239
- // Run the benchmark for a single iteration in case it's expensive.
240
- n := 1
241
-
242
238
// Signal that we're done whether we return normally
243
239
// or by FailNow's runtime.Goexit.
244
240
defer func () {
245
241
b .signal <- true
246
242
}()
247
243
248
- b .runN (n )
249
244
// Run the benchmark for at least the specified amount of time.
250
245
d := b .benchTime
251
- for ! b .failed && b .duration < d && n < 1e9 {
246
+ for n := 1 ; ! b .failed && b .duration < d && n < 1e9 ; {
252
247
last := n
253
248
// Predict required iterations.
254
249
if b .nsPerOp () == 0 {
@@ -392,18 +387,22 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc
392
387
393
388
// processBench runs bench b for the configured CPU counts and prints the results.
394
389
func (ctx * benchContext ) processBench (b * B ) {
395
- for _ , procs := range cpuList {
390
+ for i , procs := range cpuList {
396
391
runtime .GOMAXPROCS (procs )
397
392
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
- }
406
393
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
+ }
407
406
r := b .doBench ()
408
407
if b .failed {
409
408
// The output could be very long here, but probably isn't.
@@ -433,7 +432,7 @@ func (ctx *benchContext) processBench(b *B) {
433
432
// whether there were any failures.
434
433
//
435
434
// 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 .
437
436
func (b * B ) Run (name string , f func (b * B )) bool {
438
437
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
439
438
// 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 {
459
458
benchTime : b .benchTime ,
460
459
context : b .context ,
461
460
}
462
- if sub .probe (); ! sub . hasSub {
463
- b . add ( sub .run () )
461
+ if sub .run1 () {
462
+ sub .run ()
464
463
}
464
+ b .add (sub .result )
465
465
return ! sub .failed
466
466
}
467
467
0 commit comments