summaryrefslogtreecommitdiff
path: root/src/pkg/testing
diff options
context:
space:
mode:
authorTrevor Strohman <trevor.strohman@gmail.com>2009-11-19 16:35:34 -0800
committerTrevor Strohman <trevor.strohman@gmail.com>2009-11-19 16:35:34 -0800
commitf79f90ce049e525bc39622aa1d2b4ab17ceb2d49 (patch)
treeb8a0878148ef73d98e08fe7720ff3e654dc4ef14 /src/pkg/testing
parentf536e6624783673bbc9b347e43d3144dabf853fe (diff)
downloadgolang-f79f90ce049e525bc39622aa1d2b4ab17ceb2d49.tar.gz
Adds benchmark support to gotest.
No benchmarks are run unless the --benchmarks=<regexp> flag is specified on the gotest command line. This change includes sample benchmarks for regexp. % gotest --benchmarks=.* (standard test output redacted) testing.BenchmarkSimpleMatch 200000 7799 ns/op testing.BenchmarkUngroupedMatch 20000 76898 ns/op testing.BenchmarkGroupedMatch 50000 38148 ns/op R=r, rsc http://codereview.appspot.com/154173 Committer: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/pkg/testing')
-rw-r--r--src/pkg/testing/Makefile1
-rw-r--r--src/pkg/testing/benchmark.go150
-rw-r--r--src/pkg/testing/regexp_test.go30
-rw-r--r--src/pkg/testing/testing.go27
4 files changed, 208 insertions, 0 deletions
diff --git a/src/pkg/testing/Makefile b/src/pkg/testing/Makefile
index 809bb5642..ffbd11111 100644
--- a/src/pkg/testing/Makefile
+++ b/src/pkg/testing/Makefile
@@ -6,6 +6,7 @@ include $(GOROOT)/src/Make.$(GOARCH)
TARG=testing
GOFILES=\
+ benchmark.go\
regexp.go\
testing.go\
diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go
new file mode 100644
index 000000000..b6e100686
--- /dev/null
+++ b/src/pkg/testing/benchmark.go
@@ -0,0 +1,150 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "flag";
+ "fmt";
+ "os";
+ "time";
+)
+
+var matchBenchmarks = flag.String("benchmarks", "", "regular expression to select benchmarks to run")
+
+// An internal type but exported because it is cross-package; part of the implementation
+// of gotest.
+type Benchmark struct {
+ Name string;
+ F func(b *B);
+}
+
+// B is a type passed to Benchmark functions to manage benchmark
+// timing and to specify the number of iterations to run.
+type B struct {
+ N int;
+ benchmark Benchmark;
+ ns int64;
+ start int64;
+}
+
+// StartTimer starts timing a test. This function is called automatically
+// before a benchmark starts, but it can also used to resume timing after
+// a call to StopTimer.
+func (b *B) StartTimer() { b.start = time.Nanoseconds() }
+
+// StopTimer stops timing a test. This can be used to pause the timer
+// while performing complex initialization that you don't
+// want to measure.
+func (b *B) StopTimer() {
+ if b.start > 0 {
+ b.ns += time.Nanoseconds() - b.start
+ }
+ b.start = 0;
+}
+
+// ResetTimer stops the timer and sets the elapsed benchmark time to zero.
+func (b *B) ResetTimer() {
+ b.start = 0;
+ b.ns = 0;
+}
+
+func (b *B) nsPerOp() int64 {
+ if b.N <= 0 {
+ return 0
+ }
+ return b.ns / int64(b.N);
+}
+
+// runN runs a single benchmark for the specified number of iterations.
+func (b *B) runN(n int) {
+ b.N = n;
+ b.ResetTimer();
+ b.StartTimer();
+ b.benchmark.F(b);
+ b.StopTimer();
+}
+
+func min(x, y int) int {
+ if x > y {
+ return y
+ }
+ return x;
+}
+
+// roundDown10 rounds a number down to the nearest power of 10.
+func roundDown10(n int) int {
+ var tens = 0;
+ // tens = floor(log_10(n))
+ for n > 10 {
+ n = n / 10;
+ tens++;
+ }
+ // result = 10^tens
+ result := 1;
+ for i := 0; i < tens; i++ {
+ result *= 10
+ }
+ return result;
+}
+
+// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
+func roundUp(n int) int {
+ base := roundDown10(n);
+ if n < (2 * base) {
+ return 2 * base
+ }
+ if n < (5 * base) {
+ return 5 * base
+ }
+ return 10 * base;
+}
+
+// run times the benchmark function. It gradually increases the number
+// of benchmark iterations until the benchmark runs for a second in order
+// to get a reasonable measurement. It prints timing information in this form
+// testing.BenchmarkHello 100000 19 ns/op
+func (b *B) run() {
+ // Run the benchmark for a single iteration in case it's expensive.
+ n := 1;
+ b.runN(n);
+ // Run the benchmark for at least a second.
+ for b.ns < 1e9 && n < 1e9 {
+ last := n;
+ // Predict iterations/sec.
+ if b.nsPerOp() == 0 {
+ n = 1e9
+ } else {
+ n = 1e9 / int(b.nsPerOp())
+ }
+ // 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.
+ n = min(int(1.5*float(n)), 100*last);
+ // Round up to something easy to read.
+ n = roundUp(n);
+ b.runN(n);
+ }
+ fmt.Printf("%s\t%d\t%10d ns/op\n", b.benchmark.Name, b.N, b.nsPerOp());
+}
+
+// An internal function but exported because it is cross-package; part of the implementation
+// of gotest.
+func RunBenchmarks(benchmarks []Benchmark) {
+ // If no flag was specified, don't run benchmarks.
+ if len(*matchBenchmarks) == 0 {
+ return
+ }
+ re, err := CompileRegexp(*matchBenchmarks);
+ if err != "" {
+ println("invalid regexp for -benchmarks:", err);
+ os.Exit(1);
+ }
+ for _, Benchmark := range benchmarks {
+ if !re.MatchString(Benchmark.Name) {
+ continue
+ }
+ b := &B{benchmark: Benchmark};
+ b.run();
+ }
+}
diff --git a/src/pkg/testing/regexp_test.go b/src/pkg/testing/regexp_test.go
index 8f6b20c67..66139ea1e 100644
--- a/src/pkg/testing/regexp_test.go
+++ b/src/pkg/testing/regexp_test.go
@@ -279,3 +279,33 @@ func TestMatchFunction(t *T) {
matchFunctionTest(t, test.re, test.text, test.match);
}
}
+
+func BenchmarkSimpleMatch(b *B) {
+ b.StopTimer();
+ re, _ := CompileRegexp("a");
+ b.StartTimer();
+
+ for i := 0; i < b.N; i++ {
+ re.MatchString("a")
+ }
+}
+
+func BenchmarkUngroupedMatch(b *B) {
+ b.StopTimer();
+ re, _ := CompileRegexp("[a-z]+ [0-9]+ [a-z]+");
+ b.StartTimer();
+
+ for i := 0; i < b.N; i++ {
+ re.MatchString("word 123 other")
+ }
+}
+
+func BenchmarkGroupedMatch(b *B) {
+ b.StopTimer();
+ re, _ := CompileRegexp("([a-z]+) ([0-9]+) ([a-z]+)");
+ b.StartTimer();
+
+ for i := 0; i < b.N; i++ {
+ re.MatchString("word 123 other")
+ }
+}
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index 1fbc4f4a9..654d344c0 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -9,6 +9,33 @@
// where Xxx can by any alphanumeric string (but the first letter must not be in
// [a-z]) and serves to identify the test routine.
// These TestXxx routines should be declared within the package they are testing.
+//
+// Functions of the form
+// func BenchmarkXxx(*testing.B)
+// are considered benchmarks, and are executed by gotest when the -benchmarks
+// flag is provided.
+//
+// A sample benchmark function looks like this:
+// func BenchmarkHello(b *testing.B) {
+// for i := 0; i < b.N; i++ {
+// fmt.Sprintf("hello")
+// }
+// }
+// The benchmark package will vary b.N until the benchmark function lasts
+// long enough to be timed reliably. The output
+// testing.BenchmarkHello 500000 4076 ns/op
+// means that the loop ran 500000 times at a speed of 4076 ns per loop.
+//
+// If a benchmark needs some expensive setup before running, the timer
+// may be stopped:
+// func BenchmarkBigLen(b *testing.B) {
+// b.StopTimer();
+// big := NewBig();
+// b.StartTimer();
+// for i := 0; i < b.N; i++ {
+// big.Len();
+// }
+// }
package testing
import (