summaryrefslogtreecommitdiffstats
path: root/src/testing/benchmark_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/benchmark_test.go')
-rw-r--r--src/testing/benchmark_test.go213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go
new file mode 100644
index 0000000..2987170
--- /dev/null
+++ b/src/testing/benchmark_test.go
@@ -0,0 +1,213 @@
+// Copyright 2013 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_test
+
+import (
+ "bytes"
+ "runtime"
+ "sort"
+ "strings"
+ "sync/atomic"
+ "testing"
+ "text/template"
+ "time"
+)
+
+var prettyPrintTests = []struct {
+ v float64
+ expected string
+}{
+ {0, " 0 x"},
+ {1234.1, " 1234 x"},
+ {-1234.1, " -1234 x"},
+ {999.950001, " 1000 x"},
+ {999.949999, " 999.9 x"},
+ {99.9950001, " 100.0 x"},
+ {99.9949999, " 99.99 x"},
+ {-99.9949999, " -99.99 x"},
+ {0.000999950001, " 0.001000 x"},
+ {0.000999949999, " 0.0009999 x"}, // smallest case
+ {0.0000999949999, " 0.0001000 x"},
+}
+
+func TestPrettyPrint(t *testing.T) {
+ for _, tt := range prettyPrintTests {
+ buf := new(strings.Builder)
+ testing.PrettyPrint(buf, tt.v, "x")
+ if tt.expected != buf.String() {
+ t.Errorf("prettyPrint(%v): expected %q, actual %q", tt.v, tt.expected, buf.String())
+ }
+ }
+}
+
+func TestResultString(t *testing.T) {
+ // Test fractional ns/op handling
+ r := testing.BenchmarkResult{
+ N: 100,
+ T: 240 * time.Nanosecond,
+ }
+ if r.NsPerOp() != 2 {
+ t.Errorf("NsPerOp: expected 2, actual %v", r.NsPerOp())
+ }
+ if want, got := " 100\t 2.400 ns/op", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+
+ // Test sub-1 ns/op (issue #31005)
+ r.T = 40 * time.Nanosecond
+ if want, got := " 100\t 0.4000 ns/op", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+
+ // Test 0 ns/op
+ r.T = 0
+ if want, got := " 100", r.String(); want != got {
+ t.Errorf("String: expected %q, actual %q", want, got)
+ }
+}
+
+func TestRunParallel(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ testing.Benchmark(func(b *testing.B) {
+ procs := uint32(0)
+ iters := uint64(0)
+ b.SetParallelism(3)
+ b.RunParallel(func(pb *testing.PB) {
+ atomic.AddUint32(&procs, 1)
+ for pb.Next() {
+ atomic.AddUint64(&iters, 1)
+ }
+ })
+ if want := uint32(3 * runtime.GOMAXPROCS(0)); procs != want {
+ t.Errorf("got %v procs, want %v", procs, want)
+ }
+ if iters != uint64(b.N) {
+ t.Errorf("got %v iters, want %v", iters, b.N)
+ }
+ })
+}
+
+func TestRunParallelFail(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ // The function must be able to log/abort
+ // w/o crashing/deadlocking the whole benchmark.
+ b.Log("log")
+ b.Error("error")
+ })
+ })
+}
+
+func TestRunParallelFatal(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if b.N > 1 {
+ b.Fatal("error")
+ }
+ }
+ })
+ })
+}
+
+func TestRunParallelSkipNow(t *testing.T) {
+ testing.Benchmark(func(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if b.N > 1 {
+ b.SkipNow()
+ }
+ }
+ })
+ })
+}
+
+func ExampleB_RunParallel() {
+ // Parallel benchmark for text/template.Template.Execute on a single object.
+ testing.Benchmark(func(b *testing.B) {
+ templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
+ // RunParallel will create GOMAXPROCS goroutines
+ // and distribute work among them.
+ b.RunParallel(func(pb *testing.PB) {
+ // Each goroutine has its own bytes.Buffer.
+ var buf bytes.Buffer
+ for pb.Next() {
+ // The loop body is executed b.N times total across all goroutines.
+ buf.Reset()
+ templ.Execute(&buf, "World")
+ }
+ })
+ })
+}
+
+func TestReportMetric(t *testing.T) {
+ res := testing.Benchmark(func(b *testing.B) {
+ b.ReportMetric(12345, "ns/op")
+ b.ReportMetric(0.2, "frobs/op")
+ })
+ // Test built-in overriding.
+ if res.NsPerOp() != 12345 {
+ t.Errorf("NsPerOp: expected %v, actual %v", 12345, res.NsPerOp())
+ }
+ // Test stringing.
+ res.N = 1 // Make the output stable
+ want := " 1\t 12345 ns/op\t 0.2000 frobs/op"
+ if want != res.String() {
+ t.Errorf("expected %q, actual %q", want, res.String())
+ }
+}
+
+func ExampleB_ReportMetric() {
+ // This reports a custom benchmark metric relevant to a
+ // specific algorithm (in this case, sorting).
+ testing.Benchmark(func(b *testing.B) {
+ var compares int64
+ for i := 0; i < b.N; i++ {
+ s := []int{5, 4, 3, 2, 1}
+ sort.Slice(s, func(i, j int) bool {
+ compares++
+ return s[i] < s[j]
+ })
+ }
+ // This metric is per-operation, so divide by b.N and
+ // report it as a "/op" unit.
+ b.ReportMetric(float64(compares)/float64(b.N), "compares/op")
+ // This metric is per-time, so divide by b.Elapsed and
+ // report it as a "/ns" unit.
+ b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns")
+ })
+}
+
+func ExampleB_ReportMetric_parallel() {
+ // This reports a custom benchmark metric relevant to a
+ // specific algorithm (in this case, sorting) in parallel.
+ testing.Benchmark(func(b *testing.B) {
+ var compares atomic.Int64
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ s := []int{5, 4, 3, 2, 1}
+ sort.Slice(s, func(i, j int) bool {
+ // Because RunParallel runs the function many
+ // times in parallel, we must increment the
+ // counter atomically to avoid racing writes.
+ compares.Add(1)
+ return s[i] < s[j]
+ })
+ }
+ })
+
+ // NOTE: Report each metric once, after all of the parallel
+ // calls have completed.
+
+ // This metric is per-operation, so divide by b.N and
+ // report it as a "/op" unit.
+ b.ReportMetric(float64(compares.Load())/float64(b.N), "compares/op")
+ // This metric is per-time, so divide by b.Elapsed and
+ // report it as a "/ns" unit.
+ b.ReportMetric(float64(compares.Load())/float64(b.Elapsed().Nanoseconds()), "compares/ns")
+ })
+}