From 1d84b02b228cbd35660e168d26fd2801daed08fe Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Wed, 17 May 2023 17:33:15 +0300 Subject: [PATCH] runtime: introduce nextslicecap This allows to reuse the slice cap computation across specialized growslice funcs. Updates #49480 Change-Id: Ie075d9c3075659ea14c11d51a9cd4ed46aa0e961 Reviewed-on: https://go-review.googlesource.com/c/go/+/495876 Reviewed-by: Keith Randall Reviewed-by: Matthew Dempsky Run-TryBot: Egon Elbre Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor --- src/cmd/compile/internal/test/inl_test.go | 1 + src/runtime/slice.go | 67 ++++++++++++----------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go index 205b746dd89..4e34631d9b1 100644 --- a/src/cmd/compile/internal/test/inl_test.go +++ b/src/cmd/compile/internal/test/inl_test.go @@ -51,6 +51,7 @@ func TestIntendedInlining(t *testing.T) { "getMCache", "isDirectIface", "itabHashFunc", + "nextslicecap", "noescape", "pcvalueCacheKey", "readUnaligned32", diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 29e2fd5cbd5..a7d5769f473 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -177,37 +177,7 @@ func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice return slice{unsafe.Pointer(&zerobase), newLen, newLen} } - newcap := oldCap - doublecap := newcap + newcap - if newLen > doublecap { - newcap = newLen - } else { - const threshold = 256 - if oldCap < threshold { - newcap = doublecap - } else { - for { - // Transition from growing 2x for small slices - // to growing 1.25x for large slices. This formula - // gives a smooth-ish transition between the two. - newcap += (newcap + 3*threshold) >> 2 - - // We need to check `newcap >= newLen` and whether `newcap` overflowed. - // newLen is guaranteed to be larger than zero, hence - // when newcap overflows then `uint(newcap) > uint(newLen)`. - // This allows to check for both with the same comparison. - if uint(newcap) >= uint(newLen) { - break - } - } - - // Set newcap to the requested cap when - // the newcap calculation overflowed. - if newcap <= 0 { - newcap = newLen - } - } - } + newcap := nextslicecap(newLen, oldCap) var overflow bool var lenmem, newlenmem, capmem uintptr @@ -290,6 +260,41 @@ func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice return slice{p, newLen, newcap} } +// nextslicecap computes the next appropriate slice length. +func nextslicecap(newLen, oldCap int) int { + newcap := oldCap + doublecap := newcap + newcap + if newLen > doublecap { + return newLen + } + + const threshold = 256 + if oldCap < threshold { + return doublecap + } + for { + // Transition from growing 2x for small slices + // to growing 1.25x for large slices. This formula + // gives a smooth-ish transition between the two. + newcap += (newcap + 3*threshold) >> 2 + + // We need to check `newcap >= newLen` and whether `newcap` overflowed. + // newLen is guaranteed to be larger than zero, hence + // when newcap overflows then `uint(newcap) > uint(newLen)`. + // This allows to check for both with the same comparison. + if uint(newcap) >= uint(newLen) { + break + } + } + + // Set newcap to the requested cap when + // the newcap calculation overflowed. + if newcap <= 0 { + return newLen + } + return newcap +} + //go:linkname reflect_growslice reflect.growslice func reflect_growslice(et *_type, old slice, num int) slice { // Semantically equivalent to slices.Grow, except that the caller