Commit 3b091bf6 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Martin Möhrmann

runtime: use multiplication with overflow check for growslice

This improves performance for slices with an element size larger
than 32 bytes and removes loading a value from the maxElems
array for smaller element sizes.

name                 old time/op  new time/op  delta
GrowSlice/Byte       41.4ns ± 2%  41.5ns ± 1%    ~     (p=0.366 n=10+9)
GrowSlice/Int16      51.1ns ± 2%  51.0ns ± 2%    ~     (p=0.985 n=10+10)
GrowSlice/Int        64.0ns ± 1%  64.2ns ± 1%    ~     (p=0.180 n=10+10)
GrowSlice/Ptr        90.8ns ± 1%  90.7ns ± 1%    ~     (p=0.858 n=9+10)
GrowSlice/Struct/24   108ns ± 0%   108ns ± 2%    ~     (p=0.488 n=8+9)
GrowSlice/Struct/32   118ns ± 2%   117ns ± 2%    ~     (p=0.327 n=10+10)
GrowSlice/Struct/40   159ns ± 1%   148ns ± 1%  -6.87%  (p=0.000 n=10+9)

Updates #21588

Change-Id: I443b82972d379b1befa791f9ee468b3adc6bb760
Reviewed-on: https://go-review.googlesource.com/c/143798
Run-TryBot: Martin Möhrmann <martisch@uos.de>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent e85b8db6
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package runtime package runtime
import ( import (
"runtime/internal/math"
"runtime/internal/sys" "runtime/internal/sys"
"unsafe" "unsafe"
) )
...@@ -104,10 +105,11 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -104,10 +105,11 @@ func growslice(et *_type, old slice, cap int) slice {
msanread(old.array, uintptr(old.len*int(et.size))) msanread(old.array, uintptr(old.len*int(et.size)))
} }
if et.size == 0 {
if cap < old.cap { if cap < old.cap {
panic(errorString("growslice: cap out of range")) panic(errorString("growslice: cap out of range"))
} }
if et.size == 0 {
// append should not create a slice with nil pointer but non-zero len. // 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. // We assume that append doesn't need to preserve old.array in this case.
return slice{unsafe.Pointer(&zerobase), old.len, cap} return slice{unsafe.Pointer(&zerobase), old.len, cap}
...@@ -169,15 +171,14 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -169,15 +171,14 @@ func growslice(et *_type, old slice, cap int) slice {
default: default:
lenmem = uintptr(old.len) * et.size lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size newlenmem = uintptr(cap) * et.size
capmem = roundupsize(uintptr(newcap) * et.size) capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
overflow = uintptr(newcap) > maxSliceCap(et.size) capmem = roundupsize(capmem)
newcap = int(capmem / et.size) newcap = int(capmem / et.size)
} }
// The check of overflow (uintptr(newcap) > maxSliceCap(et.size)) // The check of overflow in addition to capmem > maxAlloc is needed
// in addition to capmem > _MaxMem is needed to prevent an overflow // to prevent an overflow which can be used to trigger a segfault
// which can be used to trigger a segfault on 32bit architectures // on 32bit architectures with this example program:
// with this example program:
// //
// type T [1<<27 + 1]int64 // type T [1<<27 + 1]int64
// //
...@@ -188,7 +189,7 @@ func growslice(et *_type, old slice, cap int) slice { ...@@ -188,7 +189,7 @@ func growslice(et *_type, old slice, cap int) slice {
// s = append(s, d, d, d, d) // s = append(s, d, d, d, d)
// print(len(s), "\n") // print(len(s), "\n")
// } // }
if cap < old.cap || overflow || capmem > maxAlloc { if overflow || capmem > maxAlloc {
panic(errorString("growslice: cap out of range")) panic(errorString("growslice: cap out of range"))
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment