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
5package runtime_test
6
7import (
8	"fmt"
9	"testing"
10)
11
12const N = 20
13
14func BenchmarkMakeSliceCopy(b *testing.B) {
15	const length = 32
16	var bytes = make([]byte, 8*length)
17	var ints = make([]int, length)
18	var ptrs = make([]*byte, length)
19	b.Run("mallocmove", func(b *testing.B) {
20		b.Run("Byte", func(b *testing.B) {
21			var x []byte
22			for i := 0; i < b.N; i++ {
23				x = make([]byte, len(bytes))
24				copy(x, bytes)
25			}
26		})
27		b.Run("Int", func(b *testing.B) {
28			var x []int
29			for i := 0; i < b.N; i++ {
30				x = make([]int, len(ints))
31				copy(x, ints)
32			}
33		})
34		b.Run("Ptr", func(b *testing.B) {
35			var x []*byte
36			for i := 0; i < b.N; i++ {
37				x = make([]*byte, len(ptrs))
38				copy(x, ptrs)
39			}
40
41		})
42	})
43	b.Run("makecopy", func(b *testing.B) {
44		b.Run("Byte", func(b *testing.B) {
45			var x []byte
46			for i := 0; i < b.N; i++ {
47				x = make([]byte, 8*length)
48				copy(x, bytes)
49			}
50		})
51		b.Run("Int", func(b *testing.B) {
52			var x []int
53			for i := 0; i < b.N; i++ {
54				x = make([]int, length)
55				copy(x, ints)
56			}
57		})
58		b.Run("Ptr", func(b *testing.B) {
59			var x []*byte
60			for i := 0; i < b.N; i++ {
61				x = make([]*byte, length)
62				copy(x, ptrs)
63			}
64
65		})
66	})
67	b.Run("nilappend", func(b *testing.B) {
68		b.Run("Byte", func(b *testing.B) {
69			var x []byte
70			for i := 0; i < b.N; i++ {
71				x = append([]byte(nil), bytes...)
72				_ = x
73			}
74		})
75		b.Run("Int", func(b *testing.B) {
76			var x []int
77			for i := 0; i < b.N; i++ {
78				x = append([]int(nil), ints...)
79				_ = x
80			}
81		})
82		b.Run("Ptr", func(b *testing.B) {
83			var x []*byte
84			for i := 0; i < b.N; i++ {
85				x = append([]*byte(nil), ptrs...)
86				_ = x
87			}
88		})
89	})
90}
91
92type (
93	struct24 struct{ a, b, c int64 }
94	struct32 struct{ a, b, c, d int64 }
95	struct40 struct{ a, b, c, d, e int64 }
96)
97
98func BenchmarkMakeSlice(b *testing.B) {
99	const length = 2
100	b.Run("Byte", func(b *testing.B) {
101		var x []byte
102		for i := 0; i < b.N; i++ {
103			x = make([]byte, length, 2*length)
104			_ = x
105		}
106	})
107	b.Run("Int16", func(b *testing.B) {
108		var x []int16
109		for i := 0; i < b.N; i++ {
110			x = make([]int16, length, 2*length)
111			_ = x
112		}
113	})
114	b.Run("Int", func(b *testing.B) {
115		var x []int
116		for i := 0; i < b.N; i++ {
117			x = make([]int, length, 2*length)
118			_ = x
119		}
120	})
121	b.Run("Ptr", func(b *testing.B) {
122		var x []*byte
123		for i := 0; i < b.N; i++ {
124			x = make([]*byte, length, 2*length)
125			_ = x
126		}
127	})
128	b.Run("Struct", func(b *testing.B) {
129		b.Run("24", func(b *testing.B) {
130			var x []struct24
131			for i := 0; i < b.N; i++ {
132				x = make([]struct24, length, 2*length)
133				_ = x
134			}
135		})
136		b.Run("32", func(b *testing.B) {
137			var x []struct32
138			for i := 0; i < b.N; i++ {
139				x = make([]struct32, length, 2*length)
140				_ = x
141			}
142		})
143		b.Run("40", func(b *testing.B) {
144			var x []struct40
145			for i := 0; i < b.N; i++ {
146				x = make([]struct40, length, 2*length)
147				_ = x
148			}
149		})
150
151	})
152}
153
154func BenchmarkGrowSlice(b *testing.B) {
155	b.Run("Byte", func(b *testing.B) {
156		x := make([]byte, 9)
157		for i := 0; i < b.N; i++ {
158			_ = append([]byte(nil), x...)
159		}
160	})
161	b.Run("Int16", func(b *testing.B) {
162		x := make([]int16, 9)
163		for i := 0; i < b.N; i++ {
164			_ = append([]int16(nil), x...)
165		}
166	})
167	b.Run("Int", func(b *testing.B) {
168		x := make([]int, 9)
169		for i := 0; i < b.N; i++ {
170			_ = append([]int(nil), x...)
171		}
172	})
173	b.Run("Ptr", func(b *testing.B) {
174		x := make([]*byte, 9)
175		for i := 0; i < b.N; i++ {
176			_ = append([]*byte(nil), x...)
177		}
178	})
179	b.Run("Struct", func(b *testing.B) {
180		b.Run("24", func(b *testing.B) {
181			x := make([]struct24, 9)
182			for i := 0; i < b.N; i++ {
183				_ = append([]struct24(nil), x...)
184			}
185		})
186		b.Run("32", func(b *testing.B) {
187			x := make([]struct32, 9)
188			for i := 0; i < b.N; i++ {
189				_ = append([]struct32(nil), x...)
190			}
191		})
192		b.Run("40", func(b *testing.B) {
193			x := make([]struct40, 9)
194			for i := 0; i < b.N; i++ {
195				_ = append([]struct40(nil), x...)
196			}
197		})
198
199	})
200}
201
202var (
203	SinkIntSlice        []int
204	SinkIntPointerSlice []*int
205)
206
207func BenchmarkExtendSlice(b *testing.B) {
208	var length = 4 // Use a variable to prevent stack allocation of slices.
209	b.Run("IntSlice", func(b *testing.B) {
210		s := make([]int, 0, length)
211		for i := 0; i < b.N; i++ {
212			s = append(s[:0:length/2], make([]int, length)...)
213		}
214		SinkIntSlice = s
215	})
216	b.Run("PointerSlice", func(b *testing.B) {
217		s := make([]*int, 0, length)
218		for i := 0; i < b.N; i++ {
219			s = append(s[:0:length/2], make([]*int, length)...)
220		}
221		SinkIntPointerSlice = s
222	})
223	b.Run("NoGrow", func(b *testing.B) {
224		s := make([]int, 0, length)
225		for i := 0; i < b.N; i++ {
226			s = append(s[:0:length], make([]int, length)...)
227		}
228		SinkIntSlice = s
229	})
230}
231
232func BenchmarkAppend(b *testing.B) {
233	b.StopTimer()
234	x := make([]int, 0, N)
235	b.StartTimer()
236	for i := 0; i < b.N; i++ {
237		x = x[0:0]
238		for j := 0; j < N; j++ {
239			x = append(x, j)
240		}
241	}
242}
243
244func BenchmarkAppendGrowByte(b *testing.B) {
245	for i := 0; i < b.N; i++ {
246		var x []byte
247		for j := 0; j < 1<<20; j++ {
248			x = append(x, byte(j))
249		}
250	}
251}
252
253func BenchmarkAppendGrowString(b *testing.B) {
254	var s string
255	for i := 0; i < b.N; i++ {
256		var x []string
257		for j := 0; j < 1<<20; j++ {
258			x = append(x, s)
259		}
260	}
261}
262
263func BenchmarkAppendSlice(b *testing.B) {
264	for _, length := range []int{1, 4, 7, 8, 15, 16, 32} {
265		b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) {
266			x := make([]byte, 0, N)
267			y := make([]byte, length)
268			for i := 0; i < b.N; i++ {
269				x = x[0:0]
270				x = append(x, y...)
271			}
272		})
273	}
274}
275
276var (
277	blackhole []byte
278)
279
280func BenchmarkAppendSliceLarge(b *testing.B) {
281	for _, length := range []int{1 << 10, 4 << 10, 16 << 10, 64 << 10, 256 << 10, 1024 << 10} {
282		y := make([]byte, length)
283		b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) {
284			for i := 0; i < b.N; i++ {
285				blackhole = nil
286				blackhole = append(blackhole, y...)
287			}
288		})
289	}
290}
291
292func BenchmarkAppendStr(b *testing.B) {
293	for _, str := range []string{
294		"1",
295		"1234",
296		"12345678",
297		"1234567890123456",
298		"12345678901234567890123456789012",
299	} {
300		b.Run(fmt.Sprint(len(str), "Bytes"), func(b *testing.B) {
301			x := make([]byte, 0, N)
302			for i := 0; i < b.N; i++ {
303				x = x[0:0]
304				x = append(x, str...)
305			}
306		})
307	}
308}
309
310func BenchmarkAppendSpecialCase(b *testing.B) {
311	b.StopTimer()
312	x := make([]int, 0, N)
313	b.StartTimer()
314	for i := 0; i < b.N; i++ {
315		x = x[0:0]
316		for j := 0; j < N; j++ {
317			if len(x) < cap(x) {
318				x = x[:len(x)+1]
319				x[len(x)-1] = j
320			} else {
321				x = append(x, j)
322			}
323		}
324	}
325}
326
327var x []int
328
329func f() int {
330	x[:1][0] = 3
331	return 2
332}
333
334func TestSideEffectOrder(t *testing.T) {
335	x = make([]int, 0, 10)
336	x = append(x, 1, f())
337	if x[0] != 1 || x[1] != 2 {
338		t.Error("append failed: ", x[0], x[1])
339	}
340}
341
342func TestAppendOverlap(t *testing.T) {
343	x := []byte("1234")
344	x = append(x[1:], x...) // p > q in runtime·appendslice.
345	got := string(x)
346	want := "2341234"
347	if got != want {
348		t.Errorf("overlap failed: got %q want %q", got, want)
349	}
350}
351
352func BenchmarkCopy(b *testing.B) {
353	for _, l := range []int{1, 2, 4, 8, 12, 16, 32, 128, 1024} {
354		buf := make([]byte, 4096)
355		b.Run(fmt.Sprint(l, "Byte"), func(b *testing.B) {
356			s := make([]byte, l)
357			var n int
358			for i := 0; i < b.N; i++ {
359				n = copy(buf, s)
360			}
361			b.SetBytes(int64(n))
362		})
363		b.Run(fmt.Sprint(l, "String"), func(b *testing.B) {
364			s := string(make([]byte, l))
365			var n int
366			for i := 0; i < b.N; i++ {
367				n = copy(buf, s)
368			}
369			b.SetBytes(int64(n))
370		})
371	}
372}
373
374var (
375	sByte []byte
376	s1Ptr []uintptr
377	s2Ptr [][2]uintptr
378	s3Ptr [][3]uintptr
379	s4Ptr [][4]uintptr
380)
381
382// BenchmarkAppendInPlace tests the performance of append
383// when the result is being written back to the same slice.
384// In order for the in-place optimization to occur,
385// the slice must be referred to by address;
386// using a global is an easy way to trigger that.
387// We test the "grow" and "no grow" paths separately,
388// but not the "normal" (occasionally grow) path,
389// because it is a blend of the other two.
390// We use small numbers and small sizes in an attempt
391// to avoid benchmarking memory allocation and copying.
392// We use scalars instead of pointers in an attempt
393// to avoid benchmarking the write barriers.
394// We benchmark four common sizes (byte, pointer, string/interface, slice),
395// and one larger size.
396func BenchmarkAppendInPlace(b *testing.B) {
397	b.Run("NoGrow", func(b *testing.B) {
398		const C = 128
399
400		b.Run("Byte", func(b *testing.B) {
401			for i := 0; i < b.N; i++ {
402				sByte = make([]byte, C)
403				for j := 0; j < C; j++ {
404					sByte = append(sByte, 0x77)
405				}
406			}
407		})
408
409		b.Run("1Ptr", func(b *testing.B) {
410			for i := 0; i < b.N; i++ {
411				s1Ptr = make([]uintptr, C)
412				for j := 0; j < C; j++ {
413					s1Ptr = append(s1Ptr, 0x77)
414				}
415			}
416		})
417
418		b.Run("2Ptr", func(b *testing.B) {
419			for i := 0; i < b.N; i++ {
420				s2Ptr = make([][2]uintptr, C)
421				for j := 0; j < C; j++ {
422					s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
423				}
424			}
425		})
426
427		b.Run("3Ptr", func(b *testing.B) {
428			for i := 0; i < b.N; i++ {
429				s3Ptr = make([][3]uintptr, C)
430				for j := 0; j < C; j++ {
431					s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
432				}
433			}
434		})
435
436		b.Run("4Ptr", func(b *testing.B) {
437			for i := 0; i < b.N; i++ {
438				s4Ptr = make([][4]uintptr, C)
439				for j := 0; j < C; j++ {
440					s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
441				}
442			}
443		})
444
445	})
446
447	b.Run("Grow", func(b *testing.B) {
448		const C = 5
449
450		b.Run("Byte", func(b *testing.B) {
451			for i := 0; i < b.N; i++ {
452				sByte = make([]byte, 0)
453				for j := 0; j < C; j++ {
454					sByte = append(sByte, 0x77)
455					sByte = sByte[:cap(sByte)]
456				}
457			}
458		})
459
460		b.Run("1Ptr", func(b *testing.B) {
461			for i := 0; i < b.N; i++ {
462				s1Ptr = make([]uintptr, 0)
463				for j := 0; j < C; j++ {
464					s1Ptr = append(s1Ptr, 0x77)
465					s1Ptr = s1Ptr[:cap(s1Ptr)]
466				}
467			}
468		})
469
470		b.Run("2Ptr", func(b *testing.B) {
471			for i := 0; i < b.N; i++ {
472				s2Ptr = make([][2]uintptr, 0)
473				for j := 0; j < C; j++ {
474					s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
475					s2Ptr = s2Ptr[:cap(s2Ptr)]
476				}
477			}
478		})
479
480		b.Run("3Ptr", func(b *testing.B) {
481			for i := 0; i < b.N; i++ {
482				s3Ptr = make([][3]uintptr, 0)
483				for j := 0; j < C; j++ {
484					s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
485					s3Ptr = s3Ptr[:cap(s3Ptr)]
486				}
487			}
488		})
489
490		b.Run("4Ptr", func(b *testing.B) {
491			for i := 0; i < b.N; i++ {
492				s4Ptr = make([][4]uintptr, 0)
493				for j := 0; j < C; j++ {
494					s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
495					s4Ptr = s4Ptr[:cap(s4Ptr)]
496				}
497			}
498		})
499
500	})
501}
502