Skip to content

Commit

Permalink
util/timedutil: add sync.Pools for *time.Timer and *timeutil.Timer
Browse files Browse the repository at this point in the history
We're allocating these timers frequently in hot code-paths. This reduces
channel allocation (i.e. time.Timer.C) from 3% of total allocations to
2%.
  • Loading branch information
petermattis committed Feb 7, 2017
1 parent add11bf commit f11ec1c
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 12 deletions.
4 changes: 2 additions & 2 deletions pkg/kv/dist_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,8 @@ func (ds *DistSender) sendToReplicas(
// Wait for completions. This loop will retry operations that fail
// with errors that reflect per-replica state and may succeed on
// other replicas.
var sendNextTimer timeutil.Timer
var slowTimer timeutil.Timer
sendNextTimer := timeutil.NewTimer()
slowTimer := timeutil.NewTimer()
defer sendNextTimer.Stop()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
Expand Down
6 changes: 3 additions & 3 deletions pkg/storage/replica.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ func (r *Replica) redirectOnOrAcquireLease(ctx context.Context) (LeaseStatus, *r

// Wait for the range lease to finish, or the context to expire.
pErr = func() *roachpb.Error {
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for {
Expand Down Expand Up @@ -1676,7 +1676,7 @@ func (r *Replica) beginCmds(ctx context.Context, ba *roachpb.BatchRequest) (*end
// However, the command queue assumes that commands don't drop
// out before their prerequisites, so we still have to wait it
// out.
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for _, ch := range chans {
Expand Down Expand Up @@ -2158,7 +2158,7 @@ func (r *Replica) tryAddWriteCmd(
// If the command was accepted by raft, wait for the range to apply it.
ctxDone := ctx.Done()
shouldQuiesce := r.store.stopper.ShouldQuiesce()
var slowTimer timeutil.Timer
slowTimer := timeutil.NewTimer()
defer slowTimer.Stop()
slowTimer.Reset(base.SlowRequestThreshold)
for {
Expand Down
42 changes: 35 additions & 7 deletions pkg/util/timeutil/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@

package timeutil

import "time"
import (
"sync"
"time"
)

var timerPool = sync.Pool{
New: func() interface{} {
return &Timer{}
},
}
var timeTimerPool sync.Pool

// The Timer type represents a single event. When the Timer expires,
// the current time will be sent on Timer.C.
Expand Down Expand Up @@ -53,15 +63,25 @@ type Timer struct {
Read bool
}

// NewTimer allocates a new timer.
func NewTimer() *Timer {
return timerPool.Get().(*Timer)
}

// Reset changes the timer to expire after duration d and returns
// the new value of the timer. This method includes the fix proposed
// in https://github.com/golang/go/issues/11513#issuecomment-157062583,
// but requires users of Timer to set Timer.Read to true whenever
// they successfully read from the Timer's channel. Reset operates on
// and returns a value so that Timer can be stack allocated.
// they successfully read from the Timer's channel.
func (t *Timer) Reset(d time.Duration) {
if t.timer == nil {
t.timer = time.NewTimer(d)
switch timer := timeTimerPool.Get(); timer {
case nil:
t.timer = time.NewTimer(d)
default:
t.timer = timer.(*time.Timer)
t.timer.Reset(d)
}
t.C = t.timer.C
return
}
Expand All @@ -77,8 +97,16 @@ func (t *Timer) Reset(d time.Duration) {
// or had never been initialized with a call to Timer.Reset. Stop does not
// close the channel, to prevent a read from succeeding incorrectly.
func (t *Timer) Stop() bool {
if t.timer == nil {
return false
var res bool
if t.timer != nil {
res = t.timer.Stop()
if res {
// Only place the timer back in the pool if we successfully stopped
// it. Otherwise, we'd have to read from the channel if !t.Read.
timeTimerPool.Put(t.timer)
}
t.timer = nil
}
return t.timer.Stop()
timerPool.Put(t)
return res
}

0 comments on commit f11ec1c

Please sign in to comment.