// affecting benchmark results.
type B struct {
common
+ context *benchContext
N int
previousN int // number of iterations in the previous run
previousDuration time.Duration // total duration of the previous run
return name
}
+type benchContext struct {
+ maxLen int // The largest recorded benchmark name.
+}
+
// An internal function but exported because it is cross-package; part of the implementation
// of the "go test" command.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
}
}
ok := true
+ main := &B{
+ common: common{name: "Main"},
+ context: &benchContext{
+ maxLen: maxlen,
+ },
+ }
for _, Benchmark := range bs {
- for _, procs := range cpuList {
- runtime.GOMAXPROCS(procs)
- b := &B{
- common: common{
- signal: make(chan bool),
- name: Benchmark.Name,
- },
- benchFunc: Benchmark.F,
- }
- benchName := benchmarkName(Benchmark.Name, procs)
- fmt.Printf("%-*s\t", maxlen, benchName)
- r := b.run()
- if b.failed {
- ok = false
- // 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
- // the benchmark failed.
- fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
- continue
- }
- results := r.String()
- if *benchmarkMemory || b.showAllocResult {
- results += "\t" + r.MemString()
- }
- fmt.Println(results)
- // Unlike with tests, we ignore the -chatty flag and always print output for
- // benchmarks since the output generation time will skew the results.
- if len(b.output) > 0 {
- b.trimOutput()
- fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
- }
- if p := runtime.GOMAXPROCS(-1); p != procs {
- fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
- }
+ ok = ok && expandCPU(main, Benchmark)
+ }
+ return ok
+}
+
+func expandCPU(parent *B, Benchmark InternalBenchmark) bool {
+ ok := true
+ for _, procs := range cpuList {
+ runtime.GOMAXPROCS(procs)
+ benchName := benchmarkName(Benchmark.Name, procs)
+ fmt.Printf("%-*s\t", parent.context.maxLen, benchName)
+ b := parent.runBench(Benchmark.Name, Benchmark.F)
+ r := b.result
+ if b.failed {
+ ok = false
+ // 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
+ // the benchmark failed.
+ fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
+ continue
+ }
+ results := r.String()
+ if *benchmarkMemory || b.showAllocResult {
+ results += "\t" + r.MemString()
+ }
+ fmt.Println(results)
+ // Unlike with tests, we ignore the -chatty flag and always print output for
+ // benchmarks since the output generation time will skew the results.
+ if len(b.output) > 0 {
+ b.trimOutput()
+ fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
+ }
+ if p := runtime.GOMAXPROCS(-1); p != procs {
+ fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
}
}
return ok
}
+// runBench benchmarks f as a subbenchmark with the given name. It reports
+// whether there were any failures.
+//
+// A subbenchmark is like any other benchmark. A benchmark that calls Run at
+// least once will not be measured itself and will only run for one iteration.
+func (b *B) runBench(name string, f func(b *B)) *B {
+ sub := &B{
+ common: common{
+ signal: make(chan bool),
+ name: name,
+ parent: &b.common,
+ level: b.level + 1,
+ },
+ benchFunc: f,
+ context: b.context,
+ }
+ sub.run()
+ return sub
+}
+
// trimOutput shortens the output from a benchmark, which can be very long.
func (b *B) trimOutput() {
// The output is likely to appear multiple times because the benchmark
finished bool
parent *common
+ level int // Nesting depth of test or benchmark.
name string // Name of test or benchmark.
start time.Time // Time test or benchmark started
duration time.Duration
t.finished = true
}
+// run runs f as a subtest of t called name. It reports whether f succeeded.
+// Run will block until all its parallel subtests have completed.
+func (t *T) run(name string, f func(t *T)) bool {
+ testName := name
+ if t.level > 0 {
+ testName = t.name + "/" + name
+ }
+ t = &T{
+ common: common{
+ barrier: make(chan bool),
+ signal: make(chan bool),
+ name: testName,
+ parent: &t.common,
+ level: t.level + 1,
+ },
+ context: t.context,
+ }
+
+ if *chatty {
+ fmt.Printf("=== RUN %s\n", t.name)
+ }
+ // Instead of reducing the running count of this test before calling the
+ // tRunner and increasing it afterwards, we rely on tRunner keeping the
+ // count correct. This ensures that a sequence of sequential tests runs
+ // without being preempted, even when their parent is a parallel test. This
+ // may especially reduce surprises if *parallel == 1.
+ go tRunner(t, f)
+ <-t.signal
+ return !t.failed
+}
+
// testContext holds all fields that are common to all tests. This includes
// synchronization primitives to run at most *parallel tests.
type testContext struct {
},
context: ctx,
}
-
tRunner(t, func(t *T) {
- for i := 0; i < len(tests); i++ {
+ for _, test := range tests {
// TODO: a version of this will be the Run method.
- matched, err := matchString(*match, tests[i].Name)
+ matched, err := matchString(*match, test.Name)
if err != nil {
fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
os.Exit(1)
if !matched {
continue
}
- testName := tests[i].Name
- t := &T{
- common: common{
- barrier: make(chan bool),
- signal: make(chan bool),
- name: testName,
- parent: &t.common,
- },
- context: t.context,
- }
-
- if *chatty {
- fmt.Printf("=== RUN %s\n", t.name)
- }
- // Instead of reducing the running count of this test before calling the
- // tRunner and increasing it afterwards, we rely on tRunner keeping the
- // count correct. This ensures that a sequence of sequential tests runs
- // without being preempted, even when their parent is a parallel test. This
- // may especially reduce surprises if *parallel == 1.
- go tRunner(t, tests[i].F)
- <-t.signal
+ t.run(test.Name, test.F)
}
// Run catching the signal rather than the tRunner as a separate
// goroutine to avoid adding a goroutine during the sequential