Commit 9843ca5e authored by Dmitriy Vyukov's avatar Dmitriy Vyukov Committed by Russ Cox

gotest: add -test.benchtime and -test.cpu flags.

-test.benchtime allows to specify benchmark execution time.
-test.cpu allows to execute tests/benchmarks for several
values of GOMAXPROCS.

R=r, r, rsc
CC=golang-dev
https://golang.org/cl/4662046
parent 9dd354c6
...@@ -53,7 +53,9 @@ The resulting test binary, called (for amd64) 6.out, has several flags. ...@@ -53,7 +53,9 @@ The resulting test binary, called (for amd64) 6.out, has several flags.
Usage: Usage:
6.out [-test.v] [-test.run pattern] [-test.bench pattern] \ 6.out [-test.v] [-test.run pattern] [-test.bench pattern] \
[-test.cpuprofile=cpu.out] \ [-test.cpuprofile=cpu.out] \
[-test.memprofile=mem.out] [-test.memprofilerate=1] [-test.memprofile=mem.out] [-test.memprofilerate=1] \
[-test.timeout=10] [-test.short] \
[-test.benchtime=3] [-test.cpu=1,2,3,4]
The -test.v flag causes the tests to be logged as they run. The The -test.v flag causes the tests to be logged as they run. The
-test.run flag causes only those tests whose names match the regular -test.run flag causes only those tests whose names match the regular
...@@ -93,6 +95,13 @@ The -test.timeout flag sets a timeout for the test in seconds. If the ...@@ -93,6 +95,13 @@ The -test.timeout flag sets a timeout for the test in seconds. If the
test runs for longer than that, it will panic, dumping a stack trace test runs for longer than that, it will panic, dumping a stack trace
of all existing goroutines. of all existing goroutines.
The -test.benchtime flag specifies the number of seconds to run each benchmark.
The default is one second.
The -test.cpu flag specifies a list of GOMAXPROCS values for which
the tests or benchmarks are executed. The default is the current
value of GOMAXPROCS.
For convenience, each of these -test.X flags of the test binary is For convenience, each of these -test.X flags of the test binary is
also available as the flag -X in gotest itself. Flags not listed here also available as the flag -X in gotest itself. Flags not listed here
are unaffected. For instance, the command are unaffected. For instance, the command
......
...@@ -23,6 +23,8 @@ var usageMessage = `Usage of %s: ...@@ -23,6 +23,8 @@ var usageMessage = `Usage of %s:
// These flags can be passed with or without a "test." prefix: -v or -test.v. // These flags can be passed with or without a "test." prefix: -v or -test.v.
-bench="": passes -test.bench to test -bench="": passes -test.bench to test
-benchtime=1: passes -test.benchtime to test
-cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test -cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test -memprofile="": passes -test.memprofile to test
-memprofilerate=0: passes -test.memprofilerate to test -memprofilerate=0: passes -test.memprofilerate to test
...@@ -56,6 +58,8 @@ var flagDefn = []*flagSpec{ ...@@ -56,6 +58,8 @@ var flagDefn = []*flagSpec{
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
&flagSpec{name: "bench", passToTest: true}, &flagSpec{name: "bench", passToTest: true},
&flagSpec{name: "benchtime", passToTest: true},
&flagSpec{name: "cpu", passToTest: true},
&flagSpec{name: "cpuprofile", passToTest: true}, &flagSpec{name: "cpuprofile", passToTest: true},
&flagSpec{name: "memprofile", passToTest: true}, &flagSpec{name: "memprofile", passToTest: true},
&flagSpec{name: "memprofilerate", passToTest: true}, &flagSpec{name: "memprofilerate", passToTest: true},
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
) )
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run") var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
// An internal type but exported because it is cross-package; part of the implementation // An internal type but exported because it is cross-package; part of the implementation
// of gotest. // of gotest.
...@@ -125,14 +126,15 @@ func (b *B) run() BenchmarkResult { ...@@ -125,14 +126,15 @@ func (b *B) run() BenchmarkResult {
// Run the benchmark for a single iteration in case it's expensive. // Run the benchmark for a single iteration in case it's expensive.
n := 1 n := 1
b.runN(n) b.runN(n)
// Run the benchmark for at least a second. // Run the benchmark for at least the specified amount of time.
for b.ns < 1e9 && n < 1e9 { time := int64(*benchTime * 1e9)
for b.ns < time && n < 1e9 {
last := n last := n
// Predict iterations/sec. // Predict iterations/sec.
if b.nsPerOp() == 0 { if b.nsPerOp() == 0 {
n = 1e9 n = 1e9
} else { } else {
n = 1e9 / int(b.nsPerOp()) n = int(time / b.nsPerOp())
} }
// Run more iterations than we think we'll need for a second (1.5x). // Run more iterations than we think we'll need for a second (1.5x).
// Don't grow too fast in case we had timing errors previously. // Don't grow too fast in case we had timing errors previously.
...@@ -182,7 +184,6 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark ...@@ -182,7 +184,6 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark
if len(*matchBenchmarks) == 0 { if len(*matchBenchmarks) == 0 {
return return
} }
procs := runtime.GOMAXPROCS(-1)
for _, Benchmark := range benchmarks { for _, Benchmark := range benchmarks {
matched, err := matchString(*matchBenchmarks, Benchmark.Name) matched, err := matchString(*matchBenchmarks, Benchmark.Name)
if err != nil { if err != nil {
...@@ -192,14 +193,19 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark ...@@ -192,14 +193,19 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark
if !matched { if !matched {
continue continue
} }
b := &B{benchmark: Benchmark} for _, procs := range cpuList {
r := b.run() runtime.GOMAXPROCS(procs)
print(fmt.Sprintf("%s\t%v\n", Benchmark.Name, r)) b := &B{benchmark: Benchmark}
if p := runtime.GOMAXPROCS(-1); p != procs { r := b.run()
print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", Benchmark.Name, p)) benchName := Benchmark.Name
procs = p if procs != 1 {
benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
}
print(fmt.Sprintf("%s\t%v\n", benchName, r))
if p := runtime.GOMAXPROCS(-1); p != procs {
print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p))
}
} }
} }
} }
......
...@@ -44,6 +44,8 @@ import ( ...@@ -44,6 +44,8 @@ import (
"os" "os"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"strings"
"strconv"
"time" "time"
) )
...@@ -62,6 +64,9 @@ var ( ...@@ -62,6 +64,9 @@ var (
memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds") timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
cpuList []int
) )
// Short reports whether the -test.short flag is set. // Short reports whether the -test.short flag is set.
...@@ -157,6 +162,7 @@ func tRunner(t *T, test *InternalTest) { ...@@ -157,6 +162,7 @@ func tRunner(t *T, test *InternalTest) {
// of gotest. // of gotest.
func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest, benchmarks []InternalBenchmark) { func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest, benchmarks []InternalBenchmark) {
flag.Parse() flag.Parse()
parseCpuList()
before() before()
startAlarm() startAlarm()
...@@ -171,7 +177,6 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern ...@@ -171,7 +177,6 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern
if len(tests) == 0 { if len(tests) == 0 {
println("testing: warning: no tests to run") println("testing: warning: no tests to run")
} }
procs := runtime.GOMAXPROCS(-1)
for i := 0; i < len(tests); i++ { for i := 0; i < len(tests); i++ {
matched, err := matchString(*match, tests[i].Name) matched, err := matchString(*match, tests[i].Name)
if err != nil { if err != nil {
...@@ -181,28 +186,34 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern ...@@ -181,28 +186,34 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern
if !matched { if !matched {
continue continue
} }
if *chatty { for _, procs := range cpuList {
println("=== RUN ", tests[i].Name) runtime.GOMAXPROCS(procs)
} testName := tests[i].Name
ns := -time.Nanoseconds() if procs != 1 {
t := new(T) testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
t.ch = make(chan *T) }
go tRunner(t, &tests[i]) if *chatty {
<-t.ch println("=== RUN ", testName)
ns += time.Nanoseconds() }
tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) ns := -time.Nanoseconds()
if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs { t := new(T)
t.failed = true t.ch = make(chan *T)
t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", tests[i].Name, p) go tRunner(t, &tests[i])
procs = p <-t.ch
} ns += time.Nanoseconds()
if t.failed { tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
println("--- FAIL:", tests[i].Name, tstr) if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs {
print(t.errors) t.failed = true
ok = false t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", testName, p)
} else if *chatty { }
println("--- PASS:", tests[i].Name, tstr) if t.failed {
print(t.errors) println("--- FAIL:", testName, tstr)
print(t.errors)
ok = false
} else if *chatty {
println("--- PASS:", testName, tstr)
print(t.errors)
}
} }
} }
if !ok { if !ok {
...@@ -271,3 +282,18 @@ func stopAlarm() { ...@@ -271,3 +282,18 @@ func stopAlarm() {
func alarm() { func alarm() {
panic("test timed out") panic("test timed out")
} }
func parseCpuList() {
if len(*cpuListStr) == 0 {
cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
} else {
for _, val := range strings.Split(*cpuListStr, ",", -1) {
cpu, err := strconv.Atoi(val)
if err != nil || cpu <= 0 {
println("invalid value for -test.cpu")
os.Exit(1)
}
cpuList = append(cpuList, cpu)
}
}
}
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