]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: reduce allocations for Select with up to 4 cases
authorMartin Möhrmann <moehrmann@google.com>
Tue, 28 Apr 2020 19:58:46 +0000 (21:58 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Tue, 28 Apr 2020 21:08:32 +0000 (21:08 +0000)
Allocate the runcases slice on the stack if the number
of select cases is small (up to 4).

Found while looking at production profiles of common
proto based RPC server framework code in Google which do
not have a large number of cases.

name      old time/op    new time/op    delta
Select/1     147ns ± 2%     120ns ± 6%  -18.32%  (p=0.000 n=7+10)
Select/4     316ns ± 5%     249ns ± 2%  -21.23%  (p=0.000 n=10+10)
Select/8     516ns ± 3%     515ns ± 3%     ~     (p=0.858 n=10+9)

name      old alloc/op   new alloc/op   delta
Select/1     96.0B ± 0%     64.0B ± 0%  -33.33%  (p=0.000 n=10+10)
Select/4      336B ± 0%      208B ± 0%  -38.10%  (p=0.000 n=10+10)
Select/8      672B ± 0%      672B ± 0%     ~     (all equal)

name      old allocs/op  new allocs/op  delta
Select/1      4.00 ± 0%      3.00 ± 0%  -25.00%  (p=0.000 n=10+10)
Select/4      7.00 ± 0%      6.00 ± 0%  -14.29%  (p=0.000 n=10+10)
Select/8      11.0 ± 0%      11.0 ± 0%     ~     (all equal)

Change-Id: I1687e74fc8e86606a27f03fa8a561bcfb68775d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/230657
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/reflect/all_test.go
src/reflect/value.go

index 3129ff8e5d11cc63445397a0b7dedeb8f8df6a65..b95f74354f9a1fcd03328d609bb701352fb4bf4e 100644 (file)
@@ -1680,6 +1680,26 @@ func TestSelectMaxCases(t *testing.T) {
        _, _, _ = Select(sCases)
 }
 
+func BenchmarkSelect(b *testing.B) {
+       channel := make(chan int)
+       close(channel)
+       var cases []SelectCase
+       for i := 0; i < 8; i++ {
+               cases = append(cases, SelectCase{
+                       Dir:  SelectRecv,
+                       Chan: ValueOf(channel),
+               })
+       }
+       for _, numCases := range []int{1, 4, 8} {
+               b.Run(strconv.Itoa(numCases), func(b *testing.B) {
+                       b.ReportAllocs()
+                       for i := 0; i < b.N; i++ {
+                               _, _, _ = Select(cases[:numCases])
+                       }
+               })
+       }
+}
+
 // selectWatch and the selectWatcher are a watchdog mechanism for running Select.
 // If the selectWatcher notices that the select has been blocked for >1 second, it prints
 // an error describing the select and panics the entire test binary.
index de6f22b5b3e24283a0f879c07d0ff853fe60f253..b0f06b936e77c94b22eefc9efa857be0960891a9 100644 (file)
@@ -2175,7 +2175,15 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
        // NOTE: Do not trust that caller is not modifying cases data underfoot.
        // The range is safe because the caller cannot modify our copy of the len
        // and each iteration makes its own copy of the value c.
-       runcases := make([]runtimeSelect, len(cases))
+       var runcases []runtimeSelect
+       if len(cases) > 4 {
+               // Slice is heap allocated due to runtime dependent capacity.
+               runcases = make([]runtimeSelect, len(cases))
+       } else {
+               // Slice can be stack allocated due to constant capacity.
+               runcases = make([]runtimeSelect, len(cases), 4)
+       }
+
        haveDefault := false
        for i, c := range cases {
                rc := &runcases[i]