-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
This select:
select {
case <-closedCh: // or otherwise any channel for which the receive won't block
case <-nilCh: // e.g. context.Background().Done()
}
should, ideally, be as fast as this one:
select {
case <-closedCh: // or otherwise any channel for which the receive won't block
default:
}
This is currently not the case though, with the latter being over 2 orders of magnitude faster under contention, and almost 1 with no contention.
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
│ sec/op │
Select/closed+nil-16 126.6n ± 2%
Select/closed+default-16 0.8886n ± 10%
Select/closed+nil 53.27n ± 0%
Select/closed+default 6.306n ± 0%
In selectgo there is a (pretty old) comment that claims that the code in the first example is "rare enough" that it is not worth optimizing for. I would argue that at least in code I write and maintain, a two-cases select with one of the cases being <-ctx.Done() is pretty common. This would be especially beneficial in libraries, where the library normally has no control over which Context it is passed by the host application, and where the compiler would likely be unable to statically determine that the channel is nil.
AFAICT, to address this, selectgo could - after counting how many non-nil cases exist - dispatch directly to chansend/chanrecv if there is only one non-nil case. This would be somewhat similar to what the compiler does for simple cases, but done dynamically instead of statically.
update: I actually tried it out, and to achieve the speedup it is also going to require extending the fast paths in chanrecv/chansend, but the results are promising:
Select/closed+nil-16 2.360n ± 1%
Select/closed+default-16 0.8645n ± 10%
Select/closed+nil 17.84n ± 1%
Select/closed+default 6.302n ± 0%
go version go1.21.1 darwin/amd64
Metadata
Metadata
Assignees
Labels
Type
Projects
Status