blob: fab93211b97ab53ef25308b72f954220eaa69c81 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium 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
Todd Wang8c4e5cc2015-04-09 11:30:52 -07005// Package benchmark implements utilities to augment the standard Go
6// testing.Benchmark functionality.
Jungho Ahnfa7a31f2015-01-07 17:34:12 -08007package benchmark
Jungho Ahn711a9852014-12-04 10:43:20 -08008
9import (
10 "bytes"
11 "fmt"
12 "io"
13 "math"
14 "time"
15
Jiri Simsaffceefa2015-02-28 11:03:34 -080016 "v.io/x/ref/lib/stats/histogram"
Jungho Ahn711a9852014-12-04 10:43:20 -080017)
18
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080019// Stats is a simple helper for gathering additional statistics like histogram
20// during benchmarks. This is not thread safe.
21type Stats struct {
Jungho Ahn711a9852014-12-04 10:43:20 -080022 numBuckets int
23 unit time.Duration
24 min, max int64
25 histogram *histogram.Histogram
26
27 durations durationSlice
28 dirty bool
29}
30
31type durationSlice []time.Duration
32
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080033// NewStats creates a new Stats instance. If numBuckets is not positive,
34// the default value (16) will be used.
35func NewStats(numBuckets int) *Stats {
Jungho Ahn711a9852014-12-04 10:43:20 -080036 if numBuckets <= 0 {
37 numBuckets = 16
38 }
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080039 return &Stats{
Jungho Ahn711a9852014-12-04 10:43:20 -080040 // Use one more bucket for the last unbounded bucket.
41 numBuckets: numBuckets + 1,
42 durations: make(durationSlice, 0, 100000),
43 }
44}
45
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080046// Add adds an elapsed time per operation to the stats.
47func (stats *Stats) Add(d time.Duration) {
Jungho Ahn711a9852014-12-04 10:43:20 -080048 stats.durations = append(stats.durations, d)
49 stats.dirty = true
50}
51
Jungho Ahnbc6ffa22014-12-10 17:55:35 -080052// Clear resets the stats, removing all values.
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080053func (stats *Stats) Clear() {
Jungho Ahnbc6ffa22014-12-10 17:55:35 -080054 stats.durations = stats.durations[:0]
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080055 stats.histogram = nil
56 stats.dirty = false
Jungho Ahnbc6ffa22014-12-10 17:55:35 -080057}
58
Jungho Ahn711a9852014-12-04 10:43:20 -080059// maybeUpdate updates internal stat data if there was any newly added
60// stats since this was updated.
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080061func (stats *Stats) maybeUpdate() {
62 if !stats.dirty {
Jungho Ahn711a9852014-12-04 10:43:20 -080063 return
64 }
65
66 stats.min = math.MaxInt64
67 stats.max = 0
68 for _, d := range stats.durations {
69 if stats.min > int64(d) {
70 stats.min = int64(d)
71 }
72 if stats.max < int64(d) {
73 stats.max = int64(d)
74 }
75 }
76
77 // Use the largest unit that can represent the minimum time duration.
78 stats.unit = time.Nanosecond
79 for _, u := range []time.Duration{time.Microsecond, time.Millisecond, time.Second} {
80 if stats.min <= int64(u) {
81 break
82 }
83 stats.unit = u
84 }
85
86 // Adjust the min/max according to the new unit.
87 stats.min /= int64(stats.unit)
88 stats.max /= int64(stats.unit)
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080089 numBuckets := stats.numBuckets
90 if n := int(stats.max - stats.min + 1); n < numBuckets {
91 numBuckets = n
92 }
Jungho Ahn711a9852014-12-04 10:43:20 -080093 stats.histogram = histogram.New(histogram.Options{
Jungho Ahnfa7a31f2015-01-07 17:34:12 -080094 NumBuckets: numBuckets,
Jungho Ahn711a9852014-12-04 10:43:20 -080095 // max(i.e., Nth lower bound) = min + (1 + growthFactor)^(numBuckets-2).
96 GrowthFactor: math.Pow(float64(stats.max-stats.min), 1/float64(stats.numBuckets-2)) - 1,
97 SmallestBucketSize: 1.0,
98 MinValue: stats.min})
99
100 for _, d := range stats.durations {
101 stats.histogram.Add(int64(d / stats.unit))
102 }
103
104 stats.dirty = false
105}
106
Jungho Ahnfa7a31f2015-01-07 17:34:12 -0800107// Print writes textual output of the Stats.
108func (stats *Stats) Print(w io.Writer) {
Jungho Ahn711a9852014-12-04 10:43:20 -0800109 stats.maybeUpdate()
110
Jungho Ahnfa7a31f2015-01-07 17:34:12 -0800111 if stats.histogram == nil {
112 fmt.Fprint(w, "Histogram (empty)\n")
113 } else {
114 fmt.Fprintf(w, "Histogram (unit: %s)\n", fmt.Sprintf("%v", stats.unit)[1:])
115 stats.histogram.Value().Print(w)
116 }
Jungho Ahn711a9852014-12-04 10:43:20 -0800117}
118
Jungho Ahnfa7a31f2015-01-07 17:34:12 -0800119// String returns the textual output of the Stats as string.
120func (stats *Stats) String() string {
Jungho Ahn711a9852014-12-04 10:43:20 -0800121 var b bytes.Buffer
122 stats.Print(&b)
123 return b.String()
124}