1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package check
6
7import (
8	"fmt"
9	"runtime"
10	"time"
11)
12
13var memStats runtime.MemStats
14
15// testingB is a type passed to Benchmark functions to manage benchmark
16// timing and to specify the number of iterations to run.
17type timer struct {
18	start     time.Time // Time test or benchmark started
19	duration  time.Duration
20	N         int
21	bytes     int64
22	timerOn   bool
23	benchTime time.Duration
24	// The initial states of memStats.Mallocs and memStats.TotalAlloc.
25	startAllocs uint64
26	startBytes  uint64
27	// The net total of this test after being run.
28	netAllocs uint64
29	netBytes  uint64
30}
31
32// StartTimer starts timing a test. This function is called automatically
33// before a benchmark starts, but it can also used to resume timing after
34// a call to StopTimer.
35func (c *C) StartTimer() {
36	if !c.timerOn {
37		c.start = time.Now()
38		c.timerOn = true
39
40		runtime.ReadMemStats(&memStats)
41		c.startAllocs = memStats.Mallocs
42		c.startBytes = memStats.TotalAlloc
43	}
44}
45
46// StopTimer stops timing a test. This can be used to pause the timer
47// while performing complex initialization that you don't
48// want to measure.
49func (c *C) StopTimer() {
50	if c.timerOn {
51		c.duration += time.Now().Sub(c.start)
52		c.timerOn = false
53		runtime.ReadMemStats(&memStats)
54		c.netAllocs += memStats.Mallocs - c.startAllocs
55		c.netBytes += memStats.TotalAlloc - c.startBytes
56	}
57}
58
59// ResetTimer sets the elapsed benchmark time to zero.
60// It does not affect whether the timer is running.
61func (c *C) ResetTimer() {
62	if c.timerOn {
63		c.start = time.Now()
64		runtime.ReadMemStats(&memStats)
65		c.startAllocs = memStats.Mallocs
66		c.startBytes = memStats.TotalAlloc
67	}
68	c.duration = 0
69	c.netAllocs = 0
70	c.netBytes = 0
71}
72
73// SetBytes informs the number of bytes that the benchmark processes
74// on each iteration. If this is called in a benchmark it will also
75// report MB/s.
76func (c *C) SetBytes(n int64) {
77	c.bytes = n
78}
79
80func (c *C) nsPerOp() int64 {
81	if c.N <= 0 {
82		return 0
83	}
84	return c.duration.Nanoseconds() / int64(c.N)
85}
86
87func (c *C) mbPerSec() float64 {
88	if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 {
89		return 0
90	}
91	return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds()
92}
93
94func (c *C) timerString() string {
95	if c.N <= 0 {
96		return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9)
97	}
98	mbs := c.mbPerSec()
99	mb := ""
100	if mbs != 0 {
101		mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
102	}
103	nsop := c.nsPerOp()
104	ns := fmt.Sprintf("%10d ns/op", nsop)
105	if c.N > 0 && nsop < 100 {
106		// The format specifiers here make sure that
107		// the ones digits line up for all three possible formats.
108		if nsop < 10 {
109			ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
110		} else {
111			ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
112		}
113	}
114	memStats := ""
115	if c.benchMem {
116		allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N))
117		allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N))
118		memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs)
119	}
120	return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats)
121}
122
123func min(x, y int) int {
124	if x > y {
125		return y
126	}
127	return x
128}
129
130func max(x, y int) int {
131	if x < y {
132		return y
133	}
134	return x
135}
136
137// roundDown10 rounds a number down to the nearest power of 10.
138func roundDown10(n int) int {
139	var tens = 0
140	// tens = floor(log_10(n))
141	for n > 10 {
142		n = n / 10
143		tens++
144	}
145	// result = 10^tens
146	result := 1
147	for i := 0; i < tens; i++ {
148		result *= 10
149	}
150	return result
151}
152
153// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
154func roundUp(n int) int {
155	base := roundDown10(n)
156	if n < (2 * base) {
157		return 2 * base
158	}
159	if n < (5 * base) {
160		return 5 * base
161	}
162	return 10 * base
163}
164