From 6b0688f7421aeef904d40a374bae75c37ba0b8b4 Mon Sep 17 00:00:00 2001 From: Marvin Stenger Date: Thu, 24 Mar 2016 17:42:01 +0100 Subject: [PATCH] runtime: speed up growslice by avoiding divisions 2 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is a follow-up of https://go-review.googlesource.com/#/c/20653/ Special case computation for slices with elements of byte size or pointer size. name old time/op new time/op delta GrowSliceBytes-4 86.2ns ± 3% 75.4ns ± 2% -12.50% (p=0.000 n=20+20) GrowSliceInts-4 161ns ± 3% 136ns ± 3% -15.59% (p=0.000 n=19+19) GrowSlicePtr-4 239ns ± 2% 233ns ± 2% -2.52% (p=0.000 n=20+20) GrowSliceStruct24Bytes-4 258ns ± 3% 256ns ± 3% ~ (p=0.134 n=20+20) Change-Id: Ice5fa648058fe9d7fa89dee97ca359966f671128 Reviewed-on: https://go-review.googlesource.com/21101 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/runtime/append_test.go | 24 ++++++++++++++++++++++-- src/runtime/slice.go | 32 ++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go index 4b647f70a0..3170870b0e 100644 --- a/src/runtime/append_test.go +++ b/src/runtime/append_test.go @@ -9,7 +9,7 @@ const N = 20 func BenchmarkGrowSliceBytes(b *testing.B) { b.StopTimer() - var x = make([]byte, 8) + var x = make([]byte, 9) b.StartTimer() for i := 0; i < b.N; i++ { _ = append([]byte(nil), x...) @@ -18,13 +18,33 @@ func BenchmarkGrowSliceBytes(b *testing.B) { func BenchmarkGrowSliceInts(b *testing.B) { b.StopTimer() - var x = make([]int, 8) + var x = make([]int, 9) b.StartTimer() for i := 0; i < b.N; i++ { _ = append([]int(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) { b.StopTimer() x := make([]int, 0, N) diff --git a/src/runtime/slice.go b/src/runtime/slice.go index d35ecadb16..0bc0299f72 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -56,11 +56,6 @@ func growslice(t *slicetype, old slice, cap int) slice { return slice{unsafe.Pointer(&zerobase), old.len, cap} } - maxcap := _MaxMem / et.size - if cap < old.cap || uintptr(cap) > maxcap { - panic(errorString("growslice: cap out of range")) - } - newcap := old.cap doublecap := newcap + newcap if cap > doublecap { @@ -73,17 +68,30 @@ func growslice(t *slicetype, old slice, cap int) slice { newcap += newcap / 4 } } - if uintptr(newcap) > maxcap { - panic(errorString("growslice: cap out of range")) - } } - lenmem := uintptr(old.len) * et.size - capmem := roundupsize(uintptr(newcap) * et.size) - if et.size == 1 { + var lenmem, capmem, maxcap uintptr + const ptrSize = unsafe.Sizeof((*byte)(nil)) + switch et.size { + case 1: + lenmem = uintptr(old.len) + capmem = roundupsize(uintptr(newcap)) newcap = int(capmem) - } else { + maxcap = _MaxMem + case ptrSize: + lenmem = uintptr(old.len) * ptrSize + capmem = roundupsize(uintptr(newcap) * ptrSize) + newcap = int(capmem / ptrSize) + maxcap = _MaxMem / ptrSize + default: + lenmem = uintptr(old.len) * et.size + capmem = roundupsize(uintptr(newcap) * et.size) newcap = int(capmem / et.size) + maxcap = _MaxMem / et.size + } + + if cap < old.cap || uintptr(newcap) > maxcap { + panic(errorString("growslice: cap out of range")) } var p unsafe.Pointer -- 2.48.1