]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: speed up growslice by avoiding divisions
authorMartin Möhrmann <martisch@uos.de>
Sun, 13 Mar 2016 17:58:17 +0000 (18:58 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 14 Mar 2016 00:45:57 +0000 (00:45 +0000)
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 <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/runtime/append_test.go
src/runtime/slice.go

index a67dc9b494829fe68b529485d8a1b5e03ca4e528..4b647f70a03a5c0f0efd2ba4669999f1e93d2278 100644 (file)
@@ -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)
index c67862ebacd45f7ac77b394502a16f8d139f7380..5e88ed945395fb048ac584913583121bfe03ea04 100644 (file)
@@ -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)