From 43ed65f869828f8dfc2f860b8ca1f7648e6bb93d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20Mo=CC=88hrmann?= Date: Sun, 13 Mar 2016 18:58:17 +0100 Subject: [PATCH] runtime: speed up growslice by avoiding divisions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Only compute the number of maximum allowed elements per slice once. Special case newcap computation for slices with byte sized elements. name old time/op new time/op delta GrowSliceBytes-2 61.1ns ± 1% 43.4ns ± 1% -29.00% (p=0.000 n=20+20) GrowSliceInts-2 85.9ns ± 1% 75.7ns ± 1% -11.80% (p=0.000 n=20+20) Change-Id: I5d9c0d5987cdd108ac29dc32e31912dcefa2324d Reviewed-on: https://go-review.googlesource.com/20653 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Keith Randall TryBot-Result: Gobot Gobot --- src/runtime/append_test.go | 18 ++++++++++++++++++ src/runtime/slice.go | 22 ++++++++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go index a67dc9b494..4b647f70a0 100644 --- a/src/runtime/append_test.go +++ b/src/runtime/append_test.go @@ -7,6 +7,24 @@ import "testing" const N = 20 +func BenchmarkGrowSliceBytes(b *testing.B) { + b.StopTimer() + var x = make([]byte, 8) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = append([]byte(nil), x...) + } +} + +func BenchmarkGrowSliceInts(b *testing.B) { + b.StopTimer() + var x = make([]int, 8) + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = append([]int(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 c67862ebac..5e88ed9453 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -38,10 +38,6 @@ func makeslice(t *slicetype, len64, cap64 int64) slice { // and it returns a new slice with at least that capacity, with the old data // copied into it. func growslice(t *slicetype, old slice, cap int) slice { - if cap < old.cap || t.elem.size > 0 && uintptr(cap) > _MaxMem/t.elem.size { - panic(errorString("growslice: cap out of range")) - } - if raceenabled { callerpc := getcallerpc(unsafe.Pointer(&t)) racereadrangepc(old.array, uintptr(old.len*int(t.elem.size)), callerpc, funcPC(growslice)) @@ -52,11 +48,19 @@ func growslice(t *slicetype, old slice, cap int) slice { et := t.elem if et.size == 0 { + if cap < old.cap { + panic(errorString("growslice: cap out of range")) + } // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve old.array in this case. 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 if newcap+newcap < cap { newcap = cap @@ -73,12 +77,18 @@ func growslice(t *slicetype, old slice, cap int) slice { } } - if uintptr(newcap) >= _MaxMem/et.size { + if uintptr(newcap) >= maxcap { panic(errorString("growslice: cap out of range")) } + lenmem := uintptr(old.len) * et.size capmem := roundupsize(uintptr(newcap) * et.size) - newcap = int(capmem / et.size) + if et.size == 1 { + newcap = int(capmem) + } else { + newcap = int(capmem / et.size) + } + var p unsafe.Pointer if et.kind&kindNoPointers != 0 { p = rawmem(capmem) -- 2.48.1