github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/runtime/append_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 package runtime_test 5 6 import ( 7 "fmt" 8 "testing" 9 ) 10 11 const N = 20 12 13 func BenchmarkMakeSlice(b *testing.B) { 14 var x []byte 15 for i := 0; i < b.N; i++ { 16 x = make([]byte, 32) 17 _ = x 18 } 19 } 20 21 type ( 22 struct24 struct{ a, b, c int64 } 23 struct32 struct{ a, b, c, d int64 } 24 struct40 struct{ a, b, c, d, e int64 } 25 ) 26 27 func BenchmarkGrowSlice(b *testing.B) { 28 b.Run("Byte", func(b *testing.B) { 29 x := make([]byte, 9) 30 for i := 0; i < b.N; i++ { 31 _ = append([]byte(nil), x...) 32 } 33 }) 34 b.Run("Int", func(b *testing.B) { 35 x := make([]int, 9) 36 for i := 0; i < b.N; i++ { 37 _ = append([]int(nil), x...) 38 } 39 }) 40 b.Run("Ptr", func(b *testing.B) { 41 x := make([]*byte, 9) 42 for i := 0; i < b.N; i++ { 43 _ = append([]*byte(nil), x...) 44 } 45 }) 46 b.Run("Struct", func(b *testing.B) { 47 b.Run("24", func(b *testing.B) { 48 x := make([]struct24, 9) 49 for i := 0; i < b.N; i++ { 50 _ = append([]struct24(nil), x...) 51 } 52 }) 53 b.Run("32", func(b *testing.B) { 54 x := make([]struct32, 9) 55 for i := 0; i < b.N; i++ { 56 _ = append([]struct32(nil), x...) 57 } 58 }) 59 b.Run("40", func(b *testing.B) { 60 x := make([]struct40, 9) 61 for i := 0; i < b.N; i++ { 62 _ = append([]struct40(nil), x...) 63 } 64 }) 65 66 }) 67 } 68 69 func BenchmarkAppend(b *testing.B) { 70 b.StopTimer() 71 x := make([]int, 0, N) 72 b.StartTimer() 73 for i := 0; i < b.N; i++ { 74 x = x[0:0] 75 for j := 0; j < N; j++ { 76 x = append(x, j) 77 } 78 } 79 } 80 81 func BenchmarkAppendGrowByte(b *testing.B) { 82 for i := 0; i < b.N; i++ { 83 var x []byte 84 for j := 0; j < 1<<20; j++ { 85 x = append(x, byte(j)) 86 } 87 } 88 } 89 90 func BenchmarkAppendGrowString(b *testing.B) { 91 var s string 92 for i := 0; i < b.N; i++ { 93 var x []string 94 for j := 0; j < 1<<20; j++ { 95 x = append(x, s) 96 } 97 } 98 } 99 100 func BenchmarkAppendSlice(b *testing.B) { 101 for _, length := range []int{1, 4, 7, 8, 15, 16, 32} { 102 b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) { 103 x := make([]byte, 0, N) 104 y := make([]byte, length) 105 for i := 0; i < b.N; i++ { 106 x = x[0:0] 107 x = append(x, y...) 108 } 109 }) 110 } 111 } 112 113 var ( 114 blackhole []byte 115 ) 116 117 func BenchmarkAppendSliceLarge(b *testing.B) { 118 for _, length := range []int{1 << 10, 4 << 10, 16 << 10, 64 << 10, 256 << 10, 1024 << 10} { 119 y := make([]byte, length) 120 b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) { 121 for i := 0; i < b.N; i++ { 122 blackhole = nil 123 blackhole = append(blackhole, y...) 124 } 125 }) 126 } 127 } 128 129 func BenchmarkAppendStr(b *testing.B) { 130 for _, str := range []string{ 131 "1", 132 "1234", 133 "12345678", 134 "1234567890123456", 135 "12345678901234567890123456789012", 136 } { 137 b.Run(fmt.Sprint(len(str), "Bytes"), func(b *testing.B) { 138 x := make([]byte, 0, N) 139 for i := 0; i < b.N; i++ { 140 x = x[0:0] 141 x = append(x, str...) 142 } 143 }) 144 } 145 } 146 147 func BenchmarkAppendSpecialCase(b *testing.B) { 148 b.StopTimer() 149 x := make([]int, 0, N) 150 b.StartTimer() 151 for i := 0; i < b.N; i++ { 152 x = x[0:0] 153 for j := 0; j < N; j++ { 154 if len(x) < cap(x) { 155 x = x[:len(x)+1] 156 x[len(x)-1] = j 157 } else { 158 x = append(x, j) 159 } 160 } 161 } 162 } 163 164 var x []int 165 166 func f() int { 167 x[:1][0] = 3 168 return 2 169 } 170 171 func TestSideEffectOrder(t *testing.T) { 172 x = make([]int, 0, 10) 173 x = append(x, 1, f()) 174 if x[0] != 1 || x[1] != 2 { 175 t.Error("append failed: ", x[0], x[1]) 176 } 177 } 178 179 func TestAppendOverlap(t *testing.T) { 180 x := []byte("1234") 181 x = append(x[1:], x...) // p > q in runtimeĀ·appendslice. 182 got := string(x) 183 want := "2341234" 184 if got != want { 185 t.Errorf("overlap failed: got %q want %q", got, want) 186 } 187 } 188 189 func BenchmarkCopy(b *testing.B) { 190 for _, l := range []int{1, 2, 4, 8, 12, 16, 32, 128, 1024} { 191 buf := make([]byte, 4096) 192 b.Run(fmt.Sprint(l, "Byte"), func(b *testing.B) { 193 s := make([]byte, l) 194 var n int 195 for i := 0; i < b.N; i++ { 196 n = copy(buf, s) 197 } 198 b.SetBytes(int64(n)) 199 }) 200 b.Run(fmt.Sprint(l, "String"), func(b *testing.B) { 201 s := string(make([]byte, l)) 202 var n int 203 for i := 0; i < b.N; i++ { 204 n = copy(buf, s) 205 } 206 b.SetBytes(int64(n)) 207 }) 208 } 209 } 210 211 var ( 212 sByte []byte 213 s1Ptr []uintptr 214 s2Ptr [][2]uintptr 215 s3Ptr [][3]uintptr 216 s4Ptr [][4]uintptr 217 ) 218 219 // BenchmarkAppendInPlace tests the performance of append 220 // when the result is being written back to the same slice. 221 // In order for the in-place optimization to occur, 222 // the slice must be referred to by address; 223 // using a global is an easy way to trigger that. 224 // We test the "grow" and "no grow" paths separately, 225 // but not the "normal" (occasionally grow) path, 226 // because it is a blend of the other two. 227 // We use small numbers and small sizes in an attempt 228 // to avoid benchmarking memory allocation and copying. 229 // We use scalars instead of pointers in an attempt 230 // to avoid benchmarking the write barriers. 231 // We benchmark four common sizes (byte, pointer, string/interface, slice), 232 // and one larger size. 233 func BenchmarkAppendInPlace(b *testing.B) { 234 b.Run("NoGrow", func(b *testing.B) { 235 const C = 128 236 237 b.Run("Byte", func(b *testing.B) { 238 for i := 0; i < b.N; i++ { 239 sByte = make([]byte, C) 240 for j := 0; j < C; j++ { 241 sByte = append(sByte, 0x77) 242 } 243 } 244 }) 245 246 b.Run("1Ptr", func(b *testing.B) { 247 for i := 0; i < b.N; i++ { 248 s1Ptr = make([]uintptr, C) 249 for j := 0; j < C; j++ { 250 s1Ptr = append(s1Ptr, 0x77) 251 } 252 } 253 }) 254 255 b.Run("2Ptr", func(b *testing.B) { 256 for i := 0; i < b.N; i++ { 257 s2Ptr = make([][2]uintptr, C) 258 for j := 0; j < C; j++ { 259 s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88}) 260 } 261 } 262 }) 263 264 b.Run("3Ptr", func(b *testing.B) { 265 for i := 0; i < b.N; i++ { 266 s3Ptr = make([][3]uintptr, C) 267 for j := 0; j < C; j++ { 268 s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99}) 269 } 270 } 271 }) 272 273 b.Run("4Ptr", func(b *testing.B) { 274 for i := 0; i < b.N; i++ { 275 s4Ptr = make([][4]uintptr, C) 276 for j := 0; j < C; j++ { 277 s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA}) 278 } 279 } 280 }) 281 282 }) 283 284 b.Run("Grow", func(b *testing.B) { 285 const C = 5 286 287 b.Run("Byte", func(b *testing.B) { 288 for i := 0; i < b.N; i++ { 289 sByte = make([]byte, 0) 290 for j := 0; j < C; j++ { 291 sByte = append(sByte, 0x77) 292 sByte = sByte[:cap(sByte)] 293 } 294 } 295 }) 296 297 b.Run("1Ptr", func(b *testing.B) { 298 for i := 0; i < b.N; i++ { 299 s1Ptr = make([]uintptr, 0) 300 for j := 0; j < C; j++ { 301 s1Ptr = append(s1Ptr, 0x77) 302 s1Ptr = s1Ptr[:cap(s1Ptr)] 303 } 304 } 305 }) 306 307 b.Run("2Ptr", func(b *testing.B) { 308 for i := 0; i < b.N; i++ { 309 s2Ptr = make([][2]uintptr, 0) 310 for j := 0; j < C; j++ { 311 s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88}) 312 s2Ptr = s2Ptr[:cap(s2Ptr)] 313 } 314 } 315 }) 316 317 b.Run("3Ptr", func(b *testing.B) { 318 for i := 0; i < b.N; i++ { 319 s3Ptr = make([][3]uintptr, 0) 320 for j := 0; j < C; j++ { 321 s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99}) 322 s3Ptr = s3Ptr[:cap(s3Ptr)] 323 } 324 } 325 }) 326 327 b.Run("4Ptr", func(b *testing.B) { 328 for i := 0; i < b.N; i++ { 329 s4Ptr = make([][4]uintptr, 0) 330 for j := 0; j < C; j++ { 331 s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA}) 332 s4Ptr = s4Ptr[:cap(s4Ptr)] 333 } 334 } 335 }) 336 337 }) 338 }