2009-11-19 17:35:34 -07:00
|
|
|
// 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 (
|
2009-12-15 16:41:46 -07:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2011-03-03 12:26:53 -07:00
|
|
|
"runtime"
|
2009-12-15 16:41:46 -07:00
|
|
|
"time"
|
2009-11-19 17:35:34 -07:00
|
|
|
)
|
|
|
|
|
2011-02-17 17:17:33 -07:00
|
|
|
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
|
2011-06-27 11:31:40 -06:00
|
|
|
var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
|
2009-11-19 17:35:34 -07:00
|
|
|
|
|
|
|
// An internal type but exported because it is cross-package; part of the implementation
|
|
|
|
// of gotest.
|
2010-11-01 14:15:17 -06:00
|
|
|
type InternalBenchmark struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
Name string
|
|
|
|
F func(b *B)
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// B is a type passed to Benchmark functions to manage benchmark
|
|
|
|
// timing and to specify the number of iterations to run.
|
|
|
|
type B struct {
|
2009-12-15 16:41:46 -07:00
|
|
|
N int
|
2010-11-01 14:15:17 -06:00
|
|
|
benchmark InternalBenchmark
|
2009-12-15 16:41:46 -07:00
|
|
|
ns int64
|
|
|
|
bytes int64
|
|
|
|
start int64
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2011-06-29 08:26:16 -06:00
|
|
|
func (b *B) StartTimer() {
|
|
|
|
if b.start == 0 {
|
|
|
|
b.start = time.Nanoseconds()
|
|
|
|
}
|
|
|
|
}
|
2009-11-19 17:35:34 -07:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
b.start = 0
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
2011-06-29 08:26:16 -06:00
|
|
|
// ResetTimer sets the elapsed benchmark time to zero.
|
|
|
|
// It does not affect whether the timer is running.
|
2009-11-19 17:35:34 -07:00
|
|
|
func (b *B) ResetTimer() {
|
2011-06-29 08:26:16 -06:00
|
|
|
if b.start > 0 {
|
|
|
|
b.start = time.Nanoseconds()
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
b.ns = 0
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
2009-12-04 10:56:31 -07:00
|
|
|
// SetBytes records the number of bytes processed in a single operation.
|
|
|
|
// If this is called, the benchmark will report ns/op and MB/s.
|
2009-12-15 16:41:46 -07:00
|
|
|
func (b *B) SetBytes(n int64) { b.bytes = n }
|
2009-12-04 10:56:31 -07:00
|
|
|
|
2009-11-19 17:35:34 -07:00
|
|
|
func (b *B) nsPerOp() int64 {
|
|
|
|
if b.N <= 0 {
|
|
|
|
return 0
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return b.ns / int64(b.N)
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// runN runs a single benchmark for the specified number of iterations.
|
|
|
|
func (b *B) runN(n int) {
|
2011-03-03 12:26:53 -07:00
|
|
|
// Try to get a comparable environment for each run
|
|
|
|
// by clearing garbage from previous runs.
|
|
|
|
runtime.GC()
|
2009-12-15 16:41:46 -07:00
|
|
|
b.N = n
|
|
|
|
b.ResetTimer()
|
|
|
|
b.StartTimer()
|
|
|
|
b.benchmark.F(b)
|
|
|
|
b.StopTimer()
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func min(x, y int) int {
|
|
|
|
if x > y {
|
|
|
|
return y
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return x
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
2010-02-26 18:18:43 -07:00
|
|
|
func max(x, y int) int {
|
|
|
|
if x < y {
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
2009-11-19 17:35:34 -07:00
|
|
|
// roundDown10 rounds a number down to the nearest power of 10.
|
|
|
|
func roundDown10(n int) int {
|
2009-12-15 16:41:46 -07:00
|
|
|
var tens = 0
|
2009-11-19 17:35:34 -07:00
|
|
|
// tens = floor(log_10(n))
|
|
|
|
for n > 10 {
|
2009-12-15 16:41:46 -07:00
|
|
|
n = n / 10
|
|
|
|
tens++
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
// result = 10^tens
|
2009-12-15 16:41:46 -07:00
|
|
|
result := 1
|
2009-11-19 17:35:34 -07:00
|
|
|
for i := 0; i < tens; i++ {
|
|
|
|
result *= 10
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return result
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
|
|
|
|
func roundUp(n int) int {
|
2009-12-15 16:41:46 -07:00
|
|
|
base := roundDown10(n)
|
2009-11-19 17:35:34 -07:00
|
|
|
if n < (2 * base) {
|
|
|
|
return 2 * base
|
|
|
|
}
|
|
|
|
if n < (5 * base) {
|
|
|
|
return 5 * base
|
|
|
|
}
|
2009-12-15 16:41:46 -07:00
|
|
|
return 10 * base
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2010-11-01 14:15:17 -06:00
|
|
|
func (b *B) run() BenchmarkResult {
|
2009-11-19 17:35:34 -07:00
|
|
|
// Run the benchmark for a single iteration in case it's expensive.
|
2009-12-15 16:41:46 -07:00
|
|
|
n := 1
|
|
|
|
b.runN(n)
|
2011-06-27 11:31:40 -06:00
|
|
|
// Run the benchmark for at least the specified amount of time.
|
|
|
|
time := int64(*benchTime * 1e9)
|
|
|
|
for b.ns < time && n < 1e9 {
|
2009-12-15 16:41:46 -07:00
|
|
|
last := n
|
2009-11-19 17:35:34 -07:00
|
|
|
// Predict iterations/sec.
|
|
|
|
if b.nsPerOp() == 0 {
|
|
|
|
n = 1e9
|
|
|
|
} else {
|
2011-06-27 11:31:40 -06:00
|
|
|
n = int(time / b.nsPerOp())
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
// 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.
|
2010-02-26 18:18:43 -07:00
|
|
|
// Be sure to run at least one more than last time.
|
|
|
|
n = max(min(n+n/2, 100*last), last+1)
|
2009-11-19 17:35:34 -07:00
|
|
|
// Round up to something easy to read.
|
2009-12-15 16:41:46 -07:00
|
|
|
n = roundUp(n)
|
|
|
|
b.runN(n)
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
2010-11-01 14:15:17 -06:00
|
|
|
return BenchmarkResult{b.N, b.ns, b.bytes}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The results of a benchmark run.
|
|
|
|
type BenchmarkResult struct {
|
|
|
|
N int // The number of iterations.
|
|
|
|
Ns int64 // The total time taken.
|
2011-06-02 08:52:46 -06:00
|
|
|
Bytes int64 // Bytes processed in one iteration.
|
2010-11-01 14:15:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r BenchmarkResult) NsPerOp() int64 {
|
|
|
|
if r.N <= 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return r.Ns / int64(r.N)
|
|
|
|
}
|
|
|
|
|
2011-06-02 08:52:46 -06:00
|
|
|
func (r BenchmarkResult) mbPerSec() float64 {
|
|
|
|
if r.Bytes <= 0 || r.Ns <= 0 || r.N <= 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return float64(r.Bytes) * float64(r.N) / float64(r.Ns) * 1e3
|
|
|
|
}
|
|
|
|
|
2010-11-01 14:15:17 -06:00
|
|
|
func (r BenchmarkResult) String() string {
|
2011-06-02 08:52:46 -06:00
|
|
|
mbs := r.mbPerSec()
|
2009-12-15 16:41:46 -07:00
|
|
|
mb := ""
|
2011-06-02 08:52:46 -06:00
|
|
|
if mbs != 0 {
|
|
|
|
mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
|
2009-12-04 10:56:31 -07:00
|
|
|
}
|
2011-06-27 16:50:27 -06:00
|
|
|
nsop := r.NsPerOp()
|
|
|
|
ns := fmt.Sprintf("%10d ns/op", nsop)
|
|
|
|
if r.N > 0 && nsop < 100 {
|
|
|
|
// The format specifiers here make sure that
|
|
|
|
// the ones digits line up for all three possible formats.
|
|
|
|
if nsop < 10 {
|
|
|
|
ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N))
|
|
|
|
} else {
|
|
|
|
ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// An internal function but exported because it is cross-package; part of the implementation
|
|
|
|
// of gotest.
|
2011-11-01 20:05:34 -06:00
|
|
|
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
|
2009-11-19 17:35:34 -07:00
|
|
|
// If no flag was specified, don't run benchmarks.
|
|
|
|
if len(*matchBenchmarks) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, Benchmark := range benchmarks {
|
2010-10-28 17:54:24 -06:00
|
|
|
matched, err := matchString(*matchBenchmarks, Benchmark.Name)
|
|
|
|
if err != nil {
|
2011-11-15 11:09:19 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err)
|
2010-10-28 17:54:24 -06:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if !matched {
|
2009-11-19 17:35:34 -07:00
|
|
|
continue
|
|
|
|
}
|
2011-06-27 11:31:40 -06:00
|
|
|
for _, procs := range cpuList {
|
|
|
|
runtime.GOMAXPROCS(procs)
|
|
|
|
b := &B{benchmark: Benchmark}
|
|
|
|
benchName := Benchmark.Name
|
|
|
|
if procs != 1 {
|
|
|
|
benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs)
|
|
|
|
}
|
2011-11-15 11:09:19 -07:00
|
|
|
fmt.Printf("%s\t", benchName)
|
2011-07-21 09:31:07 -06:00
|
|
|
r := b.run()
|
2011-11-15 11:09:19 -07:00
|
|
|
fmt.Printf("%v\n", r)
|
2011-06-27 11:31:40 -06:00
|
|
|
if p := runtime.GOMAXPROCS(-1); p != procs {
|
2011-11-15 11:09:19 -07:00
|
|
|
fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
|
2011-06-27 11:31:40 -06:00
|
|
|
}
|
2011-06-03 11:50:44 -06:00
|
|
|
}
|
2009-11-19 17:35:34 -07:00
|
|
|
}
|
|
|
|
}
|
2010-11-01 14:15:17 -06:00
|
|
|
|
|
|
|
// Benchmark benchmarks a single function. Useful for creating
|
|
|
|
// custom benchmarks that do not use gotest.
|
2010-11-10 10:39:26 -07:00
|
|
|
func Benchmark(f func(b *B)) BenchmarkResult {
|
|
|
|
b := &B{benchmark: InternalBenchmark{"", f}}
|
2010-11-01 14:15:17 -06:00
|
|
|
return b.run()
|
|
|
|
}
|