Skip to content

Commit

Permalink
Add zero allocation Stopwatch for both timer and histograms (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
robskillington authored Feb 19, 2017
1 parent 58c90d1 commit 3349347
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 38 deletions.
46 changes: 15 additions & 31 deletions stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,6 @@ func newTimer(
return t
}

func (t *timer) Start() Stopwatch {
return timerStopwatch{start: globalClock.Now(), timer: t}
}

func (t *timer) Record(interval time.Duration) {
if t.cachedTimer != nil {
t.cachedTimer.ReportTimer(interval)
Expand All @@ -184,6 +180,15 @@ func (t *timer) Record(interval time.Duration) {
}
}

func (t *timer) Start() Stopwatch {
return NewStopwatch(globalClock.Now(), t)
}

func (t *timer) RecordStopwatch(stopwatchStart time.Time) {
d := globalClock.Now().Sub(stopwatchStart)
t.Record(d)
}

func (t *timer) snapshot() []time.Duration {
t.unreported.RLock()
snap := make([]time.Duration, len(t.unreported.values))
Expand All @@ -194,19 +199,6 @@ func (t *timer) snapshot() []time.Duration {
return snap
}

type timerStopwatch struct {
start time.Time
timer *timer
stopped int32
}

func (s timerStopwatch) Stop() {
if atomic.CompareAndSwapInt32(&s.stopped, 0, 1) {
d := globalClock.Now().Sub(s.start)
s.timer.Record(d)
}
}

type timerNoReporterSink struct {
sync.RWMutex
timer *timer
Expand Down Expand Up @@ -375,7 +367,12 @@ func (h *histogram) RecordDuration(value time.Duration) {
}

func (h *histogram) Start() Stopwatch {
return histogramStopwatch{start: globalClock.Now(), histogram: h}
return NewStopwatch(globalClock.Now(), h)
}

func (h *histogram) RecordStopwatch(stopwatchStart time.Time) {
d := globalClock.Now().Sub(stopwatchStart)
h.RecordDuration(d)
}

type histogramBucket struct {
Expand Down Expand Up @@ -415,19 +412,6 @@ func newHistogramBucket(
return bucket
}

type histogramStopwatch struct {
start time.Time
histogram *histogram
stopped int32
}

func (s histogramStopwatch) Stop() {
if atomic.CompareAndSwapInt32(&s.stopped, 0, 1) {
d := globalClock.Now().Sub(s.start)
s.histogram.RecordDuration(d)
}
}

// NullStatsReporter is an implementation of StatsReporter than simply does nothing.
var NullStatsReporter StatsReporter = nullStatsReporter{}

Expand Down
10 changes: 7 additions & 3 deletions stats_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

package tally

import "testing"
import (
"testing"
"time"
)

func BenchmarkCounterInc(b *testing.B) {
c := &counter{}
Expand Down Expand Up @@ -66,7 +69,7 @@ func BenchmarkReportGaugeWithData(b *testing.B) {
}
}

func BenchmarkTimerInterval(b *testing.B) {
func BenchmarkTimerStopwatch(b *testing.B) {
t := &timer{
name: "bencher",
tags: nil,
Expand All @@ -84,6 +87,7 @@ func BenchmarkTimerReport(b *testing.B) {
reporter: NullStatsReporter,
}
for n := 0; n < b.N; n++ {
t.Record(1234)
start := time.Now()
t.Record(time.Since(start))
}
}
27 changes: 23 additions & 4 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,29 @@ type Histogram interface {
Start() Stopwatch
}

// Stopwatch is a helper for simpler tracking of elapsed time.
type Stopwatch interface {
// Stop records the difference between the current clock and start time.
Stop()
// Stopwatch is a helper for simpler tracking of elapsed time, use the
// Stop() method to report time elapsed since its created back to the
// timer or histogram.
type Stopwatch struct {
start time.Time
recorder StopwatchRecorder
}

// NewStopwatch creates a new immutable stopwatch for recording the start
// time to a stopwatch reporter.
func NewStopwatch(start time.Time, r StopwatchRecorder) Stopwatch {
return Stopwatch{start: start, recorder: r}
}

// Stop reports time elapsed since the stopwatch start to the recorder.
func (sw Stopwatch) Stop() {
sw.recorder.RecordStopwatch(sw.start)
}

// StopwatchRecorder is a recorder that is called when a stopwatch is
// stopped with Stop().
type StopwatchRecorder interface {
RecordStopwatch(stopwatchStart time.Time)
}

// Buckets is an interface that can represent a set of buckets
Expand Down

0 comments on commit 3349347

Please sign in to comment.