Commit 05ca3409 authored by Alberto Donizetti's avatar Alberto Donizetti

testing: only compute b.N once when passed -count > 1

When running a benchmark multiple times, instead of re-computing the
value of b.N each time, use the value found by the first run.

For

  go test -bench=. -benchtime 3s -count 2 p_test.go

on the benchmark in the linked issue; before:

  BenchmarkBenchmark-4   	     500	  10180593 ns/op
  --- BENCH: BenchmarkBenchmark-4
  	  p_test.go:13: single call took 10.111079ms
  	  p_test.go:13: single call took 1.017298685s
  	  p_test.go:13: single call took 5.090096124s
  BenchmarkBenchmark-4   	     500	  10182164 ns/op
  --- BENCH: BenchmarkBenchmark-4
  	  p_test.go:13: single call took 10.098169ms
  	  p_test.go:13: single call took 1.017712905s
  	  p_test.go:13: single call took 5.090898517s
  PASS
  ok  	command-line-arguments	12.244s

and after:

  BenchmarkBenchmark-4   	     500	  10177076 ns/op
  --- BENCH: BenchmarkBenchmark-4
  	  p_test.go:13: single call took 10.091301ms
  	  p_test.go:13: single call took 1.016943125s
  	  p_test.go:13: single call took 5.088376028s
  BenchmarkBenchmark-4   	     500	  10171497 ns/op
  --- BENCH: BenchmarkBenchmark-4
  	  p_test.go:13: single call took 10.140245ms
  	  p_test.go:13: single call took 5.085605921s
  PASS
  ok  	command-line-arguments	11.218s

Fixes #23423

Change-Id: Ie66a8c5ac43881eb8741e14105db28745b4d56d3
Reviewed-on: https://go-review.googlesource.com/110775Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 3c8545c5
...@@ -4911,10 +4911,6 @@ func TestTestRegexps(t *testing.T) { ...@@ -4911,10 +4911,6 @@ func TestTestRegexps(t *testing.T) {
x_test.go:15: LOG: Y running N=2000000000 x_test.go:15: LOG: Y running N=2000000000
--- BENCH: BenchmarkX/Y --- BENCH: BenchmarkX/Y
x_test.go:15: LOG: Y running N=1 x_test.go:15: LOG: Y running N=1
x_test.go:15: LOG: Y running N=100
x_test.go:15: LOG: Y running N=10000
x_test.go:15: LOG: Y running N=1000000
x_test.go:15: LOG: Y running N=100000000
x_test.go:15: LOG: Y running N=2000000000 x_test.go:15: LOG: Y running N=2000000000
--- BENCH: BenchmarkX --- BENCH: BenchmarkX
x_test.go:13: LOG: X running N=1 x_test.go:13: LOG: X running N=1
......
...@@ -251,27 +251,20 @@ func (b *B) run() { ...@@ -251,27 +251,20 @@ func (b *B) run() {
b.context.processBench(b) // Must call doBench. b.context.processBench(b) // Must call doBench.
} else { } else {
// Running func Benchmark. // Running func Benchmark.
b.doBench() b.doBench(0)
} }
} }
func (b *B) doBench() BenchmarkResult { func (b *B) doBench(hint int) BenchmarkResult {
go b.launch() go b.launch(hint)
<-b.signal <-b.signal
return b.result return b.result
} }
// launch launches the benchmark function. It gradually increases the number // autodetectN runs the benchmark function, gradually increasing the
// of benchmark iterations until the benchmark runs for the requested benchtime. // number of iterations until the benchmark runs for the requested
// launch is run by the doBench function as a separate goroutine. // benchtime.
// run1 must have been called on b. func (b *B) autodetectN() {
func (b *B) launch() {
// Signal that we're done whether we return normally
// or by FailNow's runtime.Goexit.
defer func() {
b.signal <- true
}()
// Run the benchmark for at least the specified amount of time. // Run the benchmark for at least the specified amount of time.
d := b.benchTime d := b.benchTime
for n := 1; !b.failed && b.duration < d && n < 1e9; { for n := 1; !b.failed && b.duration < d && n < 1e9; {
...@@ -289,6 +282,26 @@ func (b *B) launch() { ...@@ -289,6 +282,26 @@ func (b *B) launch() {
n = roundUp(n) n = roundUp(n)
b.runN(n) b.runN(n)
} }
}
// launch launches the benchmark function for hintN iterations. If
// hintN == 0, it autodetects the number of benchmark iterations based
// on the requested benchtime.
// launch is run by the doBench function as a separate goroutine.
// run1 must have been called on b.
func (b *B) launch(hintN int) {
// Signal that we're done whether we return normally
// or by FailNow's runtime.Goexit.
defer func() {
b.signal <- true
}()
if hintN == 0 {
b.autodetectN()
} else {
b.runN(hintN)
}
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes} b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes}
} }
...@@ -426,6 +439,7 @@ func runBenchmarks(importPath string, matchString func(pat, str string) (bool, e ...@@ -426,6 +439,7 @@ func runBenchmarks(importPath string, matchString func(pat, str string) (bool, e
// processBench runs bench b for the configured CPU counts and prints the results. // processBench runs bench b for the configured CPU counts and prints the results.
func (ctx *benchContext) processBench(b *B) { func (ctx *benchContext) processBench(b *B) {
for i, procs := range cpuList { for i, procs := range cpuList {
var nHint int
for j := uint(0); j < *count; j++ { for j := uint(0); j < *count; j++ {
runtime.GOMAXPROCS(procs) runtime.GOMAXPROCS(procs)
benchName := benchmarkName(b.name, procs) benchName := benchmarkName(b.name, procs)
...@@ -444,7 +458,10 @@ func (ctx *benchContext) processBench(b *B) { ...@@ -444,7 +458,10 @@ func (ctx *benchContext) processBench(b *B) {
} }
b.run1() b.run1()
} }
r := b.doBench() r := b.doBench(nHint)
if j == 0 {
nHint = b.N
}
if b.failed { if b.failed {
// The output could be very long here, but probably isn't. // The output could be very long here, but probably isn't.
// We print it all, regardless, because we don't want to trim the reason // We print it all, regardless, because we don't want to trim the reason
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment