]> Cypherpunks repositories - gostls13.git/commitdiff
testing: only compute b.N once when passed -count > 1
authorAlberto Donizetti <alb.donizetti@gmail.com>
Wed, 2 May 2018 07:45:40 +0000 (09:45 +0200)
committerAlberto Donizetti <alb.donizetti@gmail.com>
Mon, 14 May 2018 17:07:43 +0000 (17:07 +0000)
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/110775
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/go/go_test.go
src/testing/benchmark.go

index b9737688feb955601e8cfd887a18ef7243735ca0..4b68c4038209e73f340e765c3c5a3c3505a55fcf 100644 (file)
@@ -4911,10 +4911,6 @@ func TestTestRegexps(t *testing.T) {
        x_test.go:15: LOG: Y running N=2000000000
 --- BENCH: BenchmarkX/Y
        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
 --- BENCH: BenchmarkX
        x_test.go:13: LOG: X running N=1
index 4d569b79712b971bb9edb6555397cf383e24a7dd..ac9ca58397771fff0b13aa3263d7729c95d06227 100644 (file)
@@ -251,27 +251,20 @@ func (b *B) run() {
                b.context.processBench(b) // Must call doBench.
        } else {
                // Running func Benchmark.
-               b.doBench()
+               b.doBench(0)
        }
 }
 
-func (b *B) doBench() BenchmarkResult {
-       go b.launch()
+func (b *B) doBench(hint int) BenchmarkResult {
+       go b.launch(hint)
        <-b.signal
        return b.result
 }
 
-// launch launches the benchmark function. It gradually increases the number
-// of benchmark iterations until the benchmark runs for 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() {
-       // Signal that we're done whether we return normally
-       // or by FailNow's runtime.Goexit.
-       defer func() {
-               b.signal <- true
-       }()
-
+// autodetectN runs the benchmark function, gradually increasing the
+// number of iterations until the benchmark runs for the requested
+// benchtime.
+func (b *B) autodetectN() {
        // Run the benchmark for at least the specified amount of time.
        d := b.benchTime
        for n := 1; !b.failed && b.duration < d && n < 1e9; {
@@ -289,6 +282,26 @@ func (b *B) launch() {
                n = roundUp(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}
 }
 
@@ -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.
 func (ctx *benchContext) processBench(b *B) {
        for i, procs := range cpuList {
+               var nHint int
                for j := uint(0); j < *count; j++ {
                        runtime.GOMAXPROCS(procs)
                        benchName := benchmarkName(b.name, procs)
@@ -444,7 +458,10 @@ func (ctx *benchContext) processBench(b *B) {
                                }
                                b.run1()
                        }
-                       r := b.doBench()
+                       r := b.doBench(nHint)
+                       if j == 0 {
+                               nHint = b.N
+                       }
                        if b.failed {
                                // 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