]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: simplify memory capacity check in growslice
authorMartin Möhrmann <moehrmann@google.com>
Sat, 5 Aug 2017 11:44:25 +0000 (13:44 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Tue, 15 Aug 2017 04:23:11 +0000 (04:23 +0000)
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>
src/runtime/append_test.go
src/runtime/slice.go

index 6bd8f3bd951fa6e8ded0576cfe8d08818e685ff8..ef1e812c0dcfe6ea47f6ca6d08e08f1e99939787 100644 (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...)
-       }
-}
-
-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 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 }
+       struct32 struct{ a, b, c, d int64 }
+       struct40 struct{ a, b, c, d, e int64 }
+)
 
-type struct24 struct{ a, b, c int64 }
+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 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) {
index 0f49df1647e828c3db9e16979c92d82709bc6891..daaf24e721eecb98b23177df458be4f7268fe950 100644 (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"))
        }