aboutsummaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorMartin Möhrmann <moehrmann@google.com>2020-04-28 21:58:46 +0200
committerMartin Möhrmann <moehrmann@google.com>2020-04-28 21:08:32 +0000
commit17d5cef257dbb50bbe38c4f2f79cd000e01c97a6 (patch)
tree038c7c30ad10edb7403961f6668e47fd1d1d57e0 /src/reflect
parentca854f3cdaa577930b385b61571d5176193b738e (diff)
downloadgo-17d5cef257dbb50bbe38c4f2f79cd000e01c97a6.tar.gz
go-17d5cef257dbb50bbe38c4f2f79cd000e01c97a6.zip
reflect: reduce allocations for Select with up to 4 cases
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>
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/all_test.go20
-rw-r--r--src/reflect/value.go10
2 files changed, 29 insertions, 1 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 3129ff8e5d..b95f74354f 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -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.
diff --git a/src/reflect/value.go b/src/reflect/value.go
index de6f22b5b3..b0f06b936e 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -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]