]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/chan.go: improve closed channel receive performance
authorchamply <champly1993@gmail.com>
Tue, 12 Apr 2022 01:54:45 +0000 (01:54 +0000)
committerKeith Randall <khr@golang.org>
Tue, 12 Apr 2022 02:18:23 +0000 (02:18 +0000)
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 <khr@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Run-TryBot: Keith Randall <khr@google.com>
Auto-Submit: Keith Randall <khr@google.com>

src/runtime/chan.go
src/runtime/chan_test.go

index 6511d80c2c45ebc77151f2c02c0ecf1141666509..993af7063b98d75dc917e7d6475ad63620462b7e 100644 (file)
@@ -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 {
index 1e0aa53213e74aa483b503b8d181d139768d6523..a8627e98988921be59e5ce2430102d4b0173dced 100644 (file)
@@ -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() {