1
0
mirror of https://github.com/golang/go synced 2024-11-19 15:54:46 -07:00

runtime: simplify memory capacity check in growslice

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 <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Martin Möhrmann 2017-08-05 13:44:25 +02:00
parent ef6978b218
commit 365594ad59
2 changed files with 45 additions and 35 deletions

View File

@ -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) {

View File

@ -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"))
}