From ca7c6ef33d9eca2dbc7eb46601a051dc7dc4e411 Mon Sep 17 00:00:00 2001 From: champly Date: Tue, 12 Apr 2022 01:54:45 +0000 Subject: [PATCH] runtime/chan.go: improve closed channel receive performance MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use this benchmark ut: ```go func BenchmarkReceiveDataFromClosedChan(b *testing.B) { count := b.N ch := make(chan struct{}, count) for i := 0; i < count; i++ { ch <- struct{}{} } b.ResetTimer() for range ch { } } ``` Benchmark 10 times(`go test -bench=.`), and then use `benchstat` got the result: ```shell name old time/op new time/op delta ReceiveDataFromClosedChan-5 12.0ns ± 1% 11.4ns ± 0% -5.54% (p=0.000 n=10+8) ``` Fixes: #52067 Change-Id: I8db398cc8c04a46cb66ffb6768ab72a87903812f GitHub-Last-Rev: 1e0142416f223c1ebfc4a7c136bb8fca242d7934 GitHub-Pull-Request: golang/go#52068 Reviewed-on: https://go-review.googlesource.com/c/go/+/396884 Reviewed-by: Keith Randall Reviewed-by: Russ Cox Reviewed-by: Keith Randall Run-TryBot: Keith Randall Auto-Submit: Keith Randall --- src/runtime/chan.go | 36 ++++++++++++++++++++---------------- src/runtime/chan_test.go | 13 +++++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 6511d80c2c..993af7063b 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -510,24 +510,28 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) lock(&c.lock) - if c.closed != 0 && c.qcount == 0 { - if raceenabled { - raceacquire(c.raceaddr()) + if c.closed != 0 { + if c.qcount == 0 { + if raceenabled { + raceacquire(c.raceaddr()) + } + unlock(&c.lock) + if ep != nil { + typedmemclr(c.elemtype, ep) + } + return true, false } - unlock(&c.lock) - if ep != nil { - typedmemclr(c.elemtype, ep) + // The channel has been closed, but the channel's buffer have data. + } else { + // Just found waiting sender with not closed. + if sg := c.sendq.dequeue(); sg != nil { + // Found a waiting sender. If buffer is size 0, receive value + // directly from sender. Otherwise, receive from head of queue + // and add sender's value to the tail of the queue (both map to + // the same buffer slot because the queue is full). + recv(c, sg, ep, func() { unlock(&c.lock) }, 3) + return true, true } - return true, false - } - - if sg := c.sendq.dequeue(); sg != nil { - // Found a waiting sender. If buffer is size 0, receive value - // directly from sender. Otherwise, receive from head of queue - // and add sender's value to the tail of the queue (both map to - // the same buffer slot because the queue is full). - recv(c, sg, ep, func() { unlock(&c.lock) }, 3) - return true, true } if c.qcount > 0 { diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go index 1e0aa53213..a8627e9898 100644 --- a/src/runtime/chan_test.go +++ b/src/runtime/chan_test.go @@ -1127,6 +1127,19 @@ func BenchmarkSelectProdCons(b *testing.B) { } } +func BenchmarkReceiveDataFromClosedChan(b *testing.B) { + count := b.N + ch := make(chan struct{}, count) + for i := 0; i < count; i++ { + ch <- struct{}{} + } + close(ch) + + b.ResetTimer() + for range ch { + } +} + func BenchmarkChanCreation(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { -- 2.50.0