diff options
author | Josh Bleecher Snyder <josharian@gmail.com> | 2017-01-08 13:22:24 -0800 |
---|---|---|
committer | Josh Bleecher Snyder <josharian@gmail.com> | 2017-02-01 20:08:41 +0000 |
commit | 986768de7fcf4def65cecd7eb0c34e2cdf92e78c (patch) | |
tree | c3b36e104e323a14a1688321486d43cdf369b5d1 /src/context | |
parent | fd118b69fabda2f5def2f46606a0ec23f093cbb9 (diff) | |
download | go-986768de7fcf4def65cecd7eb0c34e2cdf92e78c.tar.gz go-986768de7fcf4def65cecd7eb0c34e2cdf92e78c.zip |
context: lazily initialize cancelCtx done channel
This CL reduces allocations when a context
created with WithCancel either
(1) never has its Done channel used or
(2) gets cancelled before its Done channel is used
This is not uncommon. Many contexts are created
for tasks that end up not using them.
name old time/op new time/op delta
ContextCancelTree/depth=1/Root=Background-8 112ns ± 2% 74ns ± 1% -34.03% (p=0.000 n=17+18)
ContextCancelTree/depth=1/Root=OpenCanceler-8 601ns ± 3% 544ns ± 1% -9.56% (p=0.000 n=20+20)
ContextCancelTree/depth=1/Root=ClosedCanceler-8 367ns ± 4% 257ns ± 1% -30.01% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=Background-8 2.91µs ± 2% 2.87µs ± 0% -1.38% (p=0.000 n=20+18)
ContextCancelTree/depth=10/Root=OpenCanceler-8 4.36µs ± 2% 4.26µs ± 1% -2.34% (p=0.000 n=20+18)
ContextCancelTree/depth=10/Root=ClosedCanceler-8 2.02µs ± 2% 1.51µs ± 1% -25.18% (p=0.000 n=19+19)
ContextCancelTree/depth=100/Root=Background-8 30.5µs ± 6% 30.5µs ± 1% ~ (p=0.941 n=20+20)
ContextCancelTree/depth=100/Root=OpenCanceler-8 39.8µs ± 1% 41.1µs ± 1% +3.15% (p=0.000 n=18+19)
ContextCancelTree/depth=100/Root=ClosedCanceler-8 17.8µs ± 1% 13.9µs ± 1% -21.61% (p=0.000 n=18+20)
ContextCancelTree/depth=1000/Root=Background-8 302µs ± 1% 313µs ± 0% +3.62% (p=0.000 n=20+18)
ContextCancelTree/depth=1000/Root=OpenCanceler-8 412µs ± 2% 427µs ± 1% +3.55% (p=0.000 n=18+19)
ContextCancelTree/depth=1000/Root=ClosedCanceler-8 178µs ± 1% 139µs ± 1% -21.80% (p=0.000 n=19+17)
name old alloc/op new alloc/op delta
ContextCancelTree/depth=1/Root=Background-8 176B ± 0% 80B ± 0% -54.55% (p=0.000 n=20+20)
ContextCancelTree/depth=1/Root=OpenCanceler-8 544B ± 0% 448B ± 0% -17.65% (p=0.000 n=20+20)
ContextCancelTree/depth=1/Root=ClosedCanceler-8 352B ± 0% 160B ± 0% -54.55% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=Background-8 3.49kB ± 0% 3.39kB ± 0% -2.75% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=OpenCanceler-8 3.86kB ± 0% 3.76kB ± 0% -2.49% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=ClosedCanceler-8 1.94kB ± 0% 0.88kB ± 0% -54.55% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=Background-8 36.6kB ± 0% 36.5kB ± 0% -0.26% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=OpenCanceler-8 37.0kB ± 0% 36.9kB ± 0% -0.26% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=ClosedCanceler-8 17.8kB ± 0% 8.1kB ± 0% -54.55% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=Background-8 368kB ± 0% 368kB ± 0% -0.03% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=OpenCanceler-8 368kB ± 0% 368kB ± 0% -0.03% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=ClosedCanceler-8 176kB ± 0% 80kB ± 0% -54.55% (p=0.000 n=20+20)
name old allocs/op new allocs/op delta
ContextCancelTree/depth=1/Root=Background-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=20+20)
ContextCancelTree/depth=1/Root=OpenCanceler-8 8.00 ± 0% 7.00 ± 0% -12.50% (p=0.000 n=20+20)
ContextCancelTree/depth=1/Root=ClosedCanceler-8 6.00 ± 0% 4.00 ± 0% -33.33% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=Background-8 48.0 ± 0% 47.0 ± 0% -2.08% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=OpenCanceler-8 53.0 ± 0% 52.0 ± 0% -1.89% (p=0.000 n=20+20)
ContextCancelTree/depth=10/Root=ClosedCanceler-8 33.0 ± 0% 22.0 ± 0% -33.33% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=Background-8 498 ± 0% 497 ± 0% -0.20% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=OpenCanceler-8 503 ± 0% 502 ± 0% -0.20% (p=0.000 n=20+20)
ContextCancelTree/depth=100/Root=ClosedCanceler-8 303 ± 0% 202 ± 0% -33.33% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=Background-8 5.00k ± 0% 5.00k ± 0% -0.02% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=OpenCanceler-8 5.00k ± 0% 5.00k ± 0% -0.02% (p=0.000 n=20+20)
ContextCancelTree/depth=1000/Root=ClosedCanceler-8 3.00k ± 0% 2.00k ± 0% -33.33% (p=0.000 n=20+20)
Change-Id: Ibd7a0c3d5c847861cf1497f8fead34329413d26d
Reviewed-on: https://go-review.googlesource.com/34979
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Sameer Ajmani <sameer@golang.org>
Diffstat (limited to 'src/context')
-rw-r--r-- | src/context/context.go | 31 |
1 files changed, 22 insertions, 9 deletions
diff --git a/src/context/context.go b/src/context/context.go index 0aa7c24df9..c60d378818 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -234,10 +234,7 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { // newCancelCtx returns an initialized cancelCtx. func newCancelCtx(parent Context) cancelCtx { - return cancelCtx{ - Context: parent, - done: make(chan struct{}), - } + return cancelCtx{Context: parent} } // propagateCancel arranges for child to be canceled when parent is. @@ -306,20 +303,32 @@ type canceler interface { Done() <-chan struct{} } +// closedchan is a reusable closed channel. +var closedchan = make(chan struct{}) + +func init() { + close(closedchan) +} + // A cancelCtx can be canceled. When canceled, it also cancels any children // that implement canceler. type cancelCtx struct { Context - done chan struct{} // closed by the first cancel call. - - mu sync.Mutex + mu sync.Mutex // protects following fields + done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call } func (c *cancelCtx) Done() <-chan struct{} { - return c.done + c.mu.Lock() + if c.done == nil { + c.done = make(chan struct{}) + } + d := c.done + c.mu.Unlock() + return d } func (c *cancelCtx) Err() error { @@ -344,7 +353,11 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) { return // already canceled } c.err = err - close(c.done) + if c.done == nil { + c.done = closedchan + } else { + close(c.done) + } for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err) |