From 365594ad59873cd8f7fde5ec158067bf695185ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6hrmann?= Date: Sat, 5 Aug 2017 13:44:25 +0200 Subject: [PATCH] runtime: simplify memory capacity check in growslice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of comparing if the number of elements will not fit into memory check if the memory size of the slices backing memory is higher then the memory limit. This avoids a division or maxElems lookup. With et.size > 0: uintptr(newcap) > maxSliceCap(et.size) -> uintptr(int(capmem / et.size)) > _MaxMem / et.size -> capmem / et.size > _MaxMem / et.size -> capmem > _MaxMem Note that due to integer division from capmem > _MaxMem it does not follow that uintptr(newcap) > maxSliceCap(et.size). Consolidated runtime GrowSlice benchmarks by using sub-benchmarks and added more struct sizes to show performance improvement when division is avoided for element sizes larger than 32 bytes. AMD64: GrowSlice/Byte 38.9ns ± 2% 38.9ns ± 1% ~ (p=0.974 n=20+20) GrowSlice/Int 58.3ns ± 3% 58.0ns ± 2% ~ (p=0.154 n=20+19) GrowSlice/Ptr 95.7ns ± 2% 95.1ns ± 2% -0.60% (p=0.034 n=20+20) GrowSlice/Struct/24 95.4ns ± 1% 93.9ns ± 1% -1.54% (p=0.000 n=19+19) GrowSlice/Struct/32 110ns ± 1% 108ns ± 1% -1.76% (p=0.000 n=19+20) GrowSlice/Struct/40 138ns ± 1% 128ns ± 1% -7.09% (p=0.000 n=20+20) Change-Id: I1c37857c74ea809da373e668791caffb6a5cbbd3 Reviewed-on: https://go-review.googlesource.com/53471 Run-TryBot: Martin Möhrmann TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/append_test.go | 78 +++++++++++++++++++++----------------- src/runtime/slice.go | 2 +- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go index 6bd8f3bd95..ef1e812c0d 100644 --- a/src/runtime/append_test.go +++ b/src/runtime/append_test.go @@ -18,42 +18,52 @@ func BenchmarkMakeSlice(b *testing.B) { } } -func BenchmarkGrowSliceBytes(b *testing.B) { - b.StopTimer() - var x = make([]byte, 9) - b.StartTimer() - for i := 0; i < b.N; i++ { - _ = append([]byte(nil), x...) - } -} +type ( + struct24 struct{ a, b, c int64 } + struct32 struct{ a, b, c, d int64 } + struct40 struct{ a, b, c, d, e int64 } +) -func BenchmarkGrowSliceInts(b *testing.B) { - b.StopTimer() - var x = make([]int, 9) - b.StartTimer() - for i := 0; i < b.N; i++ { - _ = append([]int(nil), x...) - } -} +func BenchmarkGrowSlice(b *testing.B) { + b.Run("Byte", func(b *testing.B) { + x := make([]byte, 9) + for i := 0; i < b.N; i++ { + _ = append([]byte(nil), x...) + } + }) + b.Run("Int", func(b *testing.B) { + x := make([]int, 9) + for i := 0; i < b.N; i++ { + _ = append([]int(nil), x...) + } + }) + b.Run("Ptr", func(b *testing.B) { + x := make([]*byte, 9) + for i := 0; i < b.N; i++ { + _ = append([]*byte(nil), x...) + } + }) + b.Run("Struct", func(b *testing.B) { + b.Run("24", func(b *testing.B) { + x := make([]struct24, 9) + for i := 0; i < b.N; i++ { + _ = append([]struct24(nil), x...) + } + }) + b.Run("32", func(b *testing.B) { + x := make([]struct32, 9) + for i := 0; i < b.N; i++ { + _ = append([]struct32(nil), x...) + } + }) + b.Run("40", func(b *testing.B) { + x := make([]struct40, 9) + for i := 0; i < b.N; i++ { + _ = append([]struct40(nil), x...) + } + }) -func BenchmarkGrowSlicePtr(b *testing.B) { - b.StopTimer() - var x = make([]*byte, 9) - b.StartTimer() - for i := 0; i < b.N; i++ { - _ = append([]*byte(nil), x...) - } -} - -type struct24 struct{ a, b, c int64 } - -func BenchmarkGrowSliceStruct24Bytes(b *testing.B) { - b.StopTimer() - var x = make([]struct24, 9) - b.StartTimer() - for i := 0; i < b.N; i++ { - _ = append([]struct24(nil), x...) - } + }) } func BenchmarkAppend(b *testing.B) { diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 0f49df1647..daaf24e721 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -131,7 +131,7 @@ func growslice(et *_type, old slice, cap int) slice { newcap = int(capmem / et.size) } - if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) { + if cap < old.cap || capmem > _MaxMem { panic(errorString("growslice: cap out of range")) }