Skip to content

Commit

Permalink
runtime: use multiplication with overflow check for makeslice
Browse files Browse the repository at this point in the history
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
MakeSlice/Byte       18.0ns ± 4%  18.0ns ± 2%     ~     (p=0.575 n=20+17)
MakeSlice/Int16      21.8ns ± 2%  21.6ns ± 1%   -0.63%  (p=0.035 n=20+19)
MakeSlice/Int        42.0ns ± 2%  41.6ns ± 1%     ~     (p=0.121 n=20+18)
MakeSlice/Ptr        62.6ns ± 2%  62.4ns ± 2%     ~     (p=0.491 n=20+18)
MakeSlice/Struct/24  57.4ns ± 3%  56.0ns ± 2%   -2.40%  (p=0.000 n=19+19)
MakeSlice/Struct/32  62.1ns ± 2%  60.6ns ± 3%   -2.43%  (p=0.000 n=20+20)
MakeSlice/Struct/40  77.3ns ± 3%  68.9ns ± 3%  -10.91%  (p=0.000 n=20+20)

Updates #21588

Change-Id: Ie12807bf8f77c0e15453413f47e3d7de771b798f
Reviewed-on: https://go-review.googlesource.com/c/142377
Run-TryBot: Martin Möhrmann <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Keith Randall <[email protected]>
  • Loading branch information
martisch committed Oct 23, 2018
1 parent 3b091bf commit 05166bf
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 20 deletions.
24 changes: 12 additions & 12 deletions src/runtime/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,21 @@ func panicmakeslicecap() {
}

func makeslice(et *_type, len, cap int) slice {
// NOTE: The len > maxElements check here is not strictly necessary,
// but it produces a 'len out of range' error instead of a 'cap out of range' error
// when someone does make([]T, bignumber). 'cap out of range' is true too,
// but since the cap is only being supplied implicitly, saying len is clearer.
// See issue 4085.
maxElements := maxSliceCap(et.size)
if len < 0 || uintptr(len) > maxElements {
panicmakeslicelen()
}

if cap < len || uintptr(cap) > maxElements {
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
if overflow || mem > maxAlloc || len < 0 || len > cap {
// NOTE: Produce a 'len out of range' error instead of a
// 'cap out of range' error when someone does make([]T, bignumber).
// 'cap out of range' is true too, but since the cap is only being
// supplied implicitly, saying len is clearer.
// See golang.org/issue/4085.
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen()
}
panicmakeslicecap()
}
p := mallocgc(mem, et, true)

p := mallocgc(et.size*uintptr(cap), et, true)
return slice{p, len, cap}
}

Expand Down
64 changes: 56 additions & 8 deletions src/runtime/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,68 @@ import (

const N = 20

func BenchmarkMakeSlice(b *testing.B) {
var x []byte
for i := 0; i < b.N; i++ {
x = make([]byte, 32)
_ = x
}
}

type (
struct24 struct{ a, b, c int64 }
struct32 struct{ a, b, c, d int64 }
struct40 struct{ a, b, c, d, e int64 }
)

func BenchmarkMakeSlice(b *testing.B) {
const length = 2
b.Run("Byte", func(b *testing.B) {
var x []byte
for i := 0; i < b.N; i++ {
x = make([]byte, length, 2*length)
_ = x
}
})
b.Run("Int16", func(b *testing.B) {
var x []int16
for i := 0; i < b.N; i++ {
x = make([]int16, length, 2*length)
_ = x
}
})
b.Run("Int", func(b *testing.B) {
var x []int
for i := 0; i < b.N; i++ {
x = make([]int, length, 2*length)
_ = x
}
})
b.Run("Ptr", func(b *testing.B) {
var x []*byte
for i := 0; i < b.N; i++ {
x = make([]*byte, length, 2*length)
_ = x
}
})
b.Run("Struct", func(b *testing.B) {
b.Run("24", func(b *testing.B) {
var x []struct24
for i := 0; i < b.N; i++ {
x = make([]struct24, length, 2*length)
_ = x
}
})
b.Run("32", func(b *testing.B) {
var x []struct32
for i := 0; i < b.N; i++ {
x = make([]struct32, length, 2*length)
_ = x
}
})
b.Run("40", func(b *testing.B) {
var x []struct40
for i := 0; i < b.N; i++ {
x = make([]struct40, length, 2*length)
_ = x
}
})

})
}

func BenchmarkGrowSlice(b *testing.B) {
b.Run("Byte", func(b *testing.B) {
x := make([]byte, 9)
Expand Down

0 comments on commit 05166bf

Please sign in to comment.