Home | History | Annotate | Download | only in runtime
      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 runtimeappendslice.
    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 }
    339