Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: add fast paths for common select patterns #64900

Open
CAFxX opened this issue Dec 30, 2023 · 1 comment
Open

runtime: add fast paths for common select patterns #64900

CAFxX opened this issue Dec 30, 2023 · 1 comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Milestone

Comments

@CAFxX
Copy link
Contributor

CAFxX commented Dec 30, 2023

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
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Dec 30, 2023
@gopherbot
Copy link

Change https://go.dev/cl/553355 mentions this issue: runtime: fast path for selects with a single non-nil case

@dmitshur dmitshur added Performance NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Jan 9, 2024
@dmitshur dmitshur added this to the Backlog milestone Jan 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Projects
Status: In Progress
Development

No branches or pull requests

3 participants