-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
Currently the runtime.scase structure is 5 words (6 words on 32-bit machines):
type scase struct {
c *hchan // chan
elem unsafe.Pointer // data element
kind uint16
pc uintptr // race pc (for race detector / msan)
releasetime int64
}
I think this can be trimmed down to just 2: c and elem. Here's how:
-
The
pcfield is only needed for race-instrumented builds. Instead of embedding it directly into thescasearray (and using stack space even for non-instrumented builds), we can split it out into a separate array that's prepended to thepollorder/lockorderarrays for race-instrumented builds, and omitted entirely for race-instrumented builds. (We'd prepend it becauseuintptrhas stricter alignment thanuint16.) -
The
releasetimefield is only needed for the case that actually succeeds. Rather than adding it to eachscase, we can just have an extracasreleasetimelocal variable that parallelscasandcasi. (I think: I'm least confident about this as I'm not familiar with the runtime's blocking profiling.) -
The
kindfield currently disambiguates four cases: send, recv, default, and nil. We can eliminate this field with a sequence of three optimizations:-
There can be only one default case. So instead of emitting a dummy scase to represent the default case,
selectgocan just take an extra boolean parameter indicating whether the call should block or not. When in non-blocking mode,selectgoshould just return-1as the case index, and callers can handle this accordingly. This would simplifyselectgo, because it wouldn't need to search for and remember where thecaseDefaultcase is. -
caseNilis used internally withinselectgoto simplify skipping over send/recv cases withcis nil. But instead of doing it this way,selectgocould simply omit nil-c cases from thepollorderandlockorderarrays when they're constructed. Then the rest of code will naturally skip over them. -
That leaves just send and recv cases. Because the runtime is going to randomize the order that cases will be processed anyway, the compiler can simply arrange that send cases always precede receive cases (or vice versa), and split
ncasesintonsendsandnrecvs. Thenselectgocan discern send and recv cases by just comparing the case index (e.g., if sends are ordered before receives, thencasi < nsendswould indicatecasis a send operation).Requiring sends before recvs will be slightly more work for
reflect.Select, but it shouldn't be too bad. It already has to translate from[]reflect.SelectCaseto[]runtime.scase(and currently it goes through an extra step and extra heap allocations withruntime.runtimeSelect). It should be easy to construct the latter by placing sends at the front of the slice and receives at the rear (remember: order in[]runtime.scasedoesn't matter), and just tracking a correspondence of array indices to translateselectgo's return value back.
-
/cc @aclements