Source file src/runtime/slice_test.go

     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  
     5  package runtime_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/asan"
    10  	"internal/goexperiment"
    11  	"internal/msan"
    12  	"internal/race"
    13  	"internal/testenv"
    14  	"runtime"
    15  	"testing"
    16  )
    17  
    18  const N = 20
    19  
    20  func BenchmarkMakeSliceCopy(b *testing.B) {
    21  	const length = 32
    22  	var bytes = make([]byte, 8*length)
    23  	var ints = make([]int, length)
    24  	var ptrs = make([]*byte, length)
    25  	b.Run("mallocmove", func(b *testing.B) {
    26  		b.Run("Byte", func(b *testing.B) {
    27  			var x []byte
    28  			for i := 0; i < b.N; i++ {
    29  				x = make([]byte, len(bytes))
    30  				copy(x, bytes)
    31  			}
    32  		})
    33  		b.Run("Int", func(b *testing.B) {
    34  			var x []int
    35  			for i := 0; i < b.N; i++ {
    36  				x = make([]int, len(ints))
    37  				copy(x, ints)
    38  			}
    39  		})
    40  		b.Run("Ptr", func(b *testing.B) {
    41  			var x []*byte
    42  			for i := 0; i < b.N; i++ {
    43  				x = make([]*byte, len(ptrs))
    44  				copy(x, ptrs)
    45  			}
    46  
    47  		})
    48  	})
    49  	b.Run("makecopy", func(b *testing.B) {
    50  		b.Run("Byte", func(b *testing.B) {
    51  			var x []byte
    52  			for i := 0; i < b.N; i++ {
    53  				x = make([]byte, 8*length)
    54  				copy(x, bytes)
    55  			}
    56  		})
    57  		b.Run("Int", func(b *testing.B) {
    58  			var x []int
    59  			for i := 0; i < b.N; i++ {
    60  				x = make([]int, length)
    61  				copy(x, ints)
    62  			}
    63  		})
    64  		b.Run("Ptr", func(b *testing.B) {
    65  			var x []*byte
    66  			for i := 0; i < b.N; i++ {
    67  				x = make([]*byte, length)
    68  				copy(x, ptrs)
    69  			}
    70  
    71  		})
    72  	})
    73  	b.Run("nilappend", func(b *testing.B) {
    74  		b.Run("Byte", func(b *testing.B) {
    75  			var x []byte
    76  			for i := 0; i < b.N; i++ {
    77  				x = append([]byte(nil), bytes...)
    78  				_ = x
    79  			}
    80  		})
    81  		b.Run("Int", func(b *testing.B) {
    82  			var x []int
    83  			for i := 0; i < b.N; i++ {
    84  				x = append([]int(nil), ints...)
    85  				_ = x
    86  			}
    87  		})
    88  		b.Run("Ptr", func(b *testing.B) {
    89  			var x []*byte
    90  			for i := 0; i < b.N; i++ {
    91  				x = append([]*byte(nil), ptrs...)
    92  				_ = x
    93  			}
    94  		})
    95  	})
    96  }
    97  
    98  type sliceHolder[T any] struct {
    99  	data [1][]T
   100  }
   101  
   102  // Benchmarks optimization when the source is a non-trivial expression.
   103  func benchmarkComplexSrc[T any](b *testing.B, typeName string, size int) {
   104  	var src = &sliceHolder[T]{}
   105  	(*src).data[0] = make([]T, size)
   106  
   107  	b.Run(typeName, func(b *testing.B) {
   108  		b.Run("mallocmove", func(b *testing.B) {
   109  			var dst []T
   110  			for i := 0; i < b.N; i++ {
   111  				dst = make([]T, len((*src).data[0]))
   112  				copy(dst, (*src).data[0])
   113  			}
   114  		})
   115  		b.Run("makecopy", func(b *testing.B) {
   116  			var dst []T
   117  			for i := 0; i < b.N; i++ {
   118  				dst = make([]T, size)
   119  				copy(dst, (*src).data[0])
   120  			}
   121  		})
   122  		b.Run("nilappend", func(b *testing.B) {
   123  			var dst []T
   124  			for i := 0; i < b.N; i++ {
   125  				dst = append([]T(nil), (*src).data[0]...)
   126  				_ = dst
   127  			}
   128  		})
   129  	})
   130  }
   131  
   132  // Benchmarks optimization when the destination is a non-trivial expression.
   133  func benchmarkComplexDst[T any](b *testing.B, typeName string, size int) {
   134  	var src = make([]T, size)
   135  
   136  	b.Run(typeName, func(b *testing.B) {
   137  		b.Run("mallocmove", func(b *testing.B) {
   138  			var dst = &sliceHolder[T]{}
   139  			for i := 0; i < b.N; i++ {
   140  				(*dst).data[0] = make([]T, len(src))
   141  				copy((*dst).data[0], src)
   142  			}
   143  		})
   144  		b.Run("makecopy", func(b *testing.B) {
   145  			var dst = &sliceHolder[T]{}
   146  			for i := 0; i < b.N; i++ {
   147  				(*dst).data[0] = make([]T, size)
   148  				copy((*dst).data[0], src)
   149  			}
   150  		})
   151  		b.Run("nilappend", func(b *testing.B) {
   152  			var dst = &sliceHolder[T]{}
   153  			for i := 0; i < b.N; i++ {
   154  				(*dst).data[0] = append([]T(nil), src...)
   155  				_ = (*dst).data[0]
   156  			}
   157  		})
   158  	})
   159  }
   160  
   161  func BenchmarkMakeSliceCopyComplexExpressions(b *testing.B) {
   162  	const length = 32
   163  
   164  	b.Run("Src", func(b *testing.B) {
   165  		benchmarkComplexSrc[byte](b, "Byte", 8*length)
   166  		benchmarkComplexSrc[int](b, "Int", length)
   167  		benchmarkComplexSrc[*byte](b, "Ptr", length)
   168  	})
   169  
   170  	b.Run("Dst", func(b *testing.B) {
   171  		benchmarkComplexDst[byte](b, "Byte", 8*length)
   172  		benchmarkComplexDst[int](b, "Int", length)
   173  		benchmarkComplexDst[*byte](b, "Ptr", length)
   174  	})
   175  }
   176  
   177  type (
   178  	struct24 struct{ a, b, c int64 }
   179  	struct32 struct{ a, b, c, d int64 }
   180  	struct40 struct{ a, b, c, d, e int64 }
   181  )
   182  
   183  func BenchmarkMakeSlice(b *testing.B) {
   184  	const length = 2
   185  	b.Run("Byte", func(b *testing.B) {
   186  		var x []byte
   187  		for i := 0; i < b.N; i++ {
   188  			x = make([]byte, length, 2*length)
   189  			_ = x
   190  		}
   191  	})
   192  	b.Run("Int16", func(b *testing.B) {
   193  		var x []int16
   194  		for i := 0; i < b.N; i++ {
   195  			x = make([]int16, length, 2*length)
   196  			_ = x
   197  		}
   198  	})
   199  	b.Run("Int", func(b *testing.B) {
   200  		var x []int
   201  		for i := 0; i < b.N; i++ {
   202  			x = make([]int, length, 2*length)
   203  			_ = x
   204  		}
   205  	})
   206  	b.Run("Ptr", func(b *testing.B) {
   207  		var x []*byte
   208  		for i := 0; i < b.N; i++ {
   209  			x = make([]*byte, length, 2*length)
   210  			_ = x
   211  		}
   212  	})
   213  	b.Run("Struct", func(b *testing.B) {
   214  		b.Run("24", func(b *testing.B) {
   215  			var x []struct24
   216  			for i := 0; i < b.N; i++ {
   217  				x = make([]struct24, length, 2*length)
   218  				_ = x
   219  			}
   220  		})
   221  		b.Run("32", func(b *testing.B) {
   222  			var x []struct32
   223  			for i := 0; i < b.N; i++ {
   224  				x = make([]struct32, length, 2*length)
   225  				_ = x
   226  			}
   227  		})
   228  		b.Run("40", func(b *testing.B) {
   229  			var x []struct40
   230  			for i := 0; i < b.N; i++ {
   231  				x = make([]struct40, length, 2*length)
   232  				_ = x
   233  			}
   234  		})
   235  
   236  	})
   237  }
   238  
   239  func BenchmarkGrowSlice(b *testing.B) {
   240  	b.Run("Byte", func(b *testing.B) {
   241  		x := make([]byte, 9)
   242  		for i := 0; i < b.N; i++ {
   243  			_ = append([]byte(nil), x...)
   244  		}
   245  	})
   246  	b.Run("Int16", func(b *testing.B) {
   247  		x := make([]int16, 9)
   248  		for i := 0; i < b.N; i++ {
   249  			_ = append([]int16(nil), x...)
   250  		}
   251  	})
   252  	b.Run("Int", func(b *testing.B) {
   253  		x := make([]int, 9)
   254  		for i := 0; i < b.N; i++ {
   255  			_ = append([]int(nil), x...)
   256  		}
   257  	})
   258  	b.Run("Ptr", func(b *testing.B) {
   259  		x := make([]*byte, 9)
   260  		for i := 0; i < b.N; i++ {
   261  			_ = append([]*byte(nil), x...)
   262  		}
   263  	})
   264  	b.Run("Struct", func(b *testing.B) {
   265  		b.Run("24", func(b *testing.B) {
   266  			x := make([]struct24, 9)
   267  			for i := 0; i < b.N; i++ {
   268  				_ = append([]struct24(nil), x...)
   269  			}
   270  		})
   271  		b.Run("32", func(b *testing.B) {
   272  			x := make([]struct32, 9)
   273  			for i := 0; i < b.N; i++ {
   274  				_ = append([]struct32(nil), x...)
   275  			}
   276  		})
   277  		b.Run("40", func(b *testing.B) {
   278  			x := make([]struct40, 9)
   279  			for i := 0; i < b.N; i++ {
   280  				_ = append([]struct40(nil), x...)
   281  			}
   282  		})
   283  
   284  	})
   285  }
   286  
   287  var (
   288  	SinkIntSlice        []int
   289  	SinkIntPointerSlice []*int
   290  )
   291  
   292  func BenchmarkExtendSlice(b *testing.B) {
   293  	var length = 4 // Use a variable to prevent stack allocation of slices.
   294  	b.Run("IntSlice", func(b *testing.B) {
   295  		s := make([]int, 0, length)
   296  		for i := 0; i < b.N; i++ {
   297  			s = append(s[:0:length/2], make([]int, length)...)
   298  		}
   299  		SinkIntSlice = s
   300  	})
   301  	b.Run("PointerSlice", func(b *testing.B) {
   302  		s := make([]*int, 0, length)
   303  		for i := 0; i < b.N; i++ {
   304  			s = append(s[:0:length/2], make([]*int, length)...)
   305  		}
   306  		SinkIntPointerSlice = s
   307  	})
   308  	b.Run("NoGrow", func(b *testing.B) {
   309  		s := make([]int, 0, length)
   310  		for i := 0; i < b.N; i++ {
   311  			s = append(s[:0:length], make([]int, length)...)
   312  		}
   313  		SinkIntSlice = s
   314  	})
   315  }
   316  
   317  func BenchmarkAppend(b *testing.B) {
   318  	b.StopTimer()
   319  	x := make([]int, 0, N)
   320  	b.StartTimer()
   321  	for i := 0; i < b.N; i++ {
   322  		x = x[0:0]
   323  		for j := 0; j < N; j++ {
   324  			x = append(x, j)
   325  		}
   326  	}
   327  }
   328  
   329  func BenchmarkAppendGrowByte(b *testing.B) {
   330  	for i := 0; i < b.N; i++ {
   331  		var x []byte
   332  		for j := 0; j < 1<<20; j++ {
   333  			x = append(x, byte(j))
   334  		}
   335  	}
   336  }
   337  
   338  func BenchmarkAppendGrowString(b *testing.B) {
   339  	var s string
   340  	for i := 0; i < b.N; i++ {
   341  		var x []string
   342  		for j := 0; j < 1<<20; j++ {
   343  			x = append(x, s)
   344  		}
   345  	}
   346  }
   347  
   348  func BenchmarkAppendSlice(b *testing.B) {
   349  	for _, length := range []int{1, 4, 7, 8, 15, 16, 32} {
   350  		b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) {
   351  			x := make([]byte, 0, N)
   352  			y := make([]byte, length)
   353  			for i := 0; i < b.N; i++ {
   354  				x = x[0:0]
   355  				x = append(x, y...)
   356  			}
   357  		})
   358  	}
   359  }
   360  
   361  var (
   362  	blackhole []byte
   363  )
   364  
   365  func BenchmarkAppendSliceLarge(b *testing.B) {
   366  	for _, length := range []int{1 << 10, 4 << 10, 16 << 10, 64 << 10, 256 << 10, 1024 << 10} {
   367  		y := make([]byte, length)
   368  		b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) {
   369  			for i := 0; i < b.N; i++ {
   370  				blackhole = nil
   371  				blackhole = append(blackhole, y...)
   372  			}
   373  		})
   374  	}
   375  }
   376  
   377  func BenchmarkAppendStr(b *testing.B) {
   378  	for _, str := range []string{
   379  		"1",
   380  		"1234",
   381  		"12345678",
   382  		"1234567890123456",
   383  		"12345678901234567890123456789012",
   384  	} {
   385  		b.Run(fmt.Sprint(len(str), "Bytes"), func(b *testing.B) {
   386  			x := make([]byte, 0, N)
   387  			for i := 0; i < b.N; i++ {
   388  				x = x[0:0]
   389  				x = append(x, str...)
   390  			}
   391  		})
   392  	}
   393  }
   394  
   395  func BenchmarkAppendSpecialCase(b *testing.B) {
   396  	b.StopTimer()
   397  	x := make([]int, 0, N)
   398  	b.StartTimer()
   399  	for i := 0; i < b.N; i++ {
   400  		x = x[0:0]
   401  		for j := 0; j < N; j++ {
   402  			if len(x) < cap(x) {
   403  				x = x[:len(x)+1]
   404  				x[len(x)-1] = j
   405  			} else {
   406  				x = append(x, j)
   407  			}
   408  		}
   409  	}
   410  }
   411  
   412  var x []int
   413  
   414  func f() int {
   415  	x[:1][0] = 3
   416  	return 2
   417  }
   418  
   419  func TestSideEffectOrder(t *testing.T) {
   420  	x = make([]int, 0, 10)
   421  	x = append(x, 1, f())
   422  	if x[0] != 1 || x[1] != 2 {
   423  		t.Error("append failed: ", x[0], x[1])
   424  	}
   425  }
   426  
   427  func TestAppendOverlap(t *testing.T) {
   428  	x := []byte("1234")
   429  	x = append(x[1:], x...) // p > q in runtimeĀ·appendslice.
   430  	got := string(x)
   431  	want := "2341234"
   432  	if got != want {
   433  		t.Errorf("overlap failed: got %q want %q", got, want)
   434  	}
   435  }
   436  
   437  func BenchmarkCopy(b *testing.B) {
   438  	for _, l := range []int{1, 2, 4, 8, 12, 16, 32, 128, 1024} {
   439  		buf := make([]byte, 4096)
   440  		b.Run(fmt.Sprint(l, "Byte"), func(b *testing.B) {
   441  			s := make([]byte, l)
   442  			var n int
   443  			for i := 0; i < b.N; i++ {
   444  				n = copy(buf, s)
   445  			}
   446  			b.SetBytes(int64(n))
   447  		})
   448  		b.Run(fmt.Sprint(l, "String"), func(b *testing.B) {
   449  			s := string(make([]byte, l))
   450  			var n int
   451  			for i := 0; i < b.N; i++ {
   452  				n = copy(buf, s)
   453  			}
   454  			b.SetBytes(int64(n))
   455  		})
   456  	}
   457  }
   458  
   459  var (
   460  	sByte []byte
   461  	s1Ptr []uintptr
   462  	s2Ptr [][2]uintptr
   463  	s3Ptr [][3]uintptr
   464  	s4Ptr [][4]uintptr
   465  )
   466  
   467  // BenchmarkAppendInPlace tests the performance of append
   468  // when the result is being written back to the same slice.
   469  // In order for the in-place optimization to occur,
   470  // the slice must be referred to by address;
   471  // using a global is an easy way to trigger that.
   472  // We test the "grow" and "no grow" paths separately,
   473  // but not the "normal" (occasionally grow) path,
   474  // because it is a blend of the other two.
   475  // We use small numbers and small sizes in an attempt
   476  // to avoid benchmarking memory allocation and copying.
   477  // We use scalars instead of pointers in an attempt
   478  // to avoid benchmarking the write barriers.
   479  // We benchmark four common sizes (byte, pointer, string/interface, slice),
   480  // and one larger size.
   481  func BenchmarkAppendInPlace(b *testing.B) {
   482  	b.Run("NoGrow", func(b *testing.B) {
   483  		const C = 128
   484  
   485  		b.Run("Byte", func(b *testing.B) {
   486  			for i := 0; i < b.N; i++ {
   487  				sByte = make([]byte, C)
   488  				for j := 0; j < C; j++ {
   489  					sByte = append(sByte, 0x77)
   490  				}
   491  			}
   492  		})
   493  
   494  		b.Run("1Ptr", func(b *testing.B) {
   495  			for i := 0; i < b.N; i++ {
   496  				s1Ptr = make([]uintptr, C)
   497  				for j := 0; j < C; j++ {
   498  					s1Ptr = append(s1Ptr, 0x77)
   499  				}
   500  			}
   501  		})
   502  
   503  		b.Run("2Ptr", func(b *testing.B) {
   504  			for i := 0; i < b.N; i++ {
   505  				s2Ptr = make([][2]uintptr, C)
   506  				for j := 0; j < C; j++ {
   507  					s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
   508  				}
   509  			}
   510  		})
   511  
   512  		b.Run("3Ptr", func(b *testing.B) {
   513  			for i := 0; i < b.N; i++ {
   514  				s3Ptr = make([][3]uintptr, C)
   515  				for j := 0; j < C; j++ {
   516  					s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
   517  				}
   518  			}
   519  		})
   520  
   521  		b.Run("4Ptr", func(b *testing.B) {
   522  			for i := 0; i < b.N; i++ {
   523  				s4Ptr = make([][4]uintptr, C)
   524  				for j := 0; j < C; j++ {
   525  					s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
   526  				}
   527  			}
   528  		})
   529  
   530  	})
   531  
   532  	b.Run("Grow", func(b *testing.B) {
   533  		const C = 5
   534  
   535  		b.Run("Byte", func(b *testing.B) {
   536  			for i := 0; i < b.N; i++ {
   537  				sByte = make([]byte, 0)
   538  				for j := 0; j < C; j++ {
   539  					sByte = append(sByte, 0x77)
   540  					sByte = sByte[:cap(sByte)]
   541  				}
   542  			}
   543  		})
   544  
   545  		b.Run("1Ptr", func(b *testing.B) {
   546  			for i := 0; i < b.N; i++ {
   547  				s1Ptr = make([]uintptr, 0)
   548  				for j := 0; j < C; j++ {
   549  					s1Ptr = append(s1Ptr, 0x77)
   550  					s1Ptr = s1Ptr[:cap(s1Ptr)]
   551  				}
   552  			}
   553  		})
   554  
   555  		b.Run("2Ptr", func(b *testing.B) {
   556  			for i := 0; i < b.N; i++ {
   557  				s2Ptr = make([][2]uintptr, 0)
   558  				for j := 0; j < C; j++ {
   559  					s2Ptr = append(s2Ptr, [2]uintptr{0x77, 0x88})
   560  					s2Ptr = s2Ptr[:cap(s2Ptr)]
   561  				}
   562  			}
   563  		})
   564  
   565  		b.Run("3Ptr", func(b *testing.B) {
   566  			for i := 0; i < b.N; i++ {
   567  				s3Ptr = make([][3]uintptr, 0)
   568  				for j := 0; j < C; j++ {
   569  					s3Ptr = append(s3Ptr, [3]uintptr{0x77, 0x88, 0x99})
   570  					s3Ptr = s3Ptr[:cap(s3Ptr)]
   571  				}
   572  			}
   573  		})
   574  
   575  		b.Run("4Ptr", func(b *testing.B) {
   576  			for i := 0; i < b.N; i++ {
   577  				s4Ptr = make([][4]uintptr, 0)
   578  				for j := 0; j < C; j++ {
   579  					s4Ptr = append(s4Ptr, [4]uintptr{0x77, 0x88, 0x99, 0xAA})
   580  					s4Ptr = s4Ptr[:cap(s4Ptr)]
   581  				}
   582  			}
   583  		})
   584  
   585  	})
   586  }
   587  
   588  //go:noinline
   589  func byteSlice(n int) []byte {
   590  	var r []byte
   591  	for i := range n {
   592  		r = append(r, byte(i))
   593  	}
   594  	return r
   595  }
   596  func TestAppendByteInLoop(t *testing.T) {
   597  	testenv.SkipIfOptimizationOff(t)
   598  	if race.Enabled {
   599  		t.Skip("skipping in -race mode")
   600  	}
   601  	if asan.Enabled || msan.Enabled {
   602  		t.Skip("skipping in sanitizer mode")
   603  	}
   604  	for _, test := range [][3]int{
   605  		{0, 0, 0},
   606  		{1, 1, 8},
   607  		{2, 1, 8},
   608  		{8, 1, 8},
   609  		{9, 1, 16},
   610  		{16, 1, 16},
   611  		{17, 1, 24},
   612  		{24, 1, 24},
   613  		{25, 1, 32},
   614  		{32, 1, 32},
   615  		{33, 1, 64}, // If we up the stack buffer size from 32->64, this line and the next would become 48.
   616  		{48, 1, 64},
   617  		{49, 1, 64},
   618  		{64, 1, 64},
   619  		{65, 2, 128},
   620  	} {
   621  		n := test[0]
   622  		want := test[1]
   623  		wantCap := test[2]
   624  
   625  		if goexperiment.RuntimeFreegc && n > 64 {
   626  			// Only 1 allocation is expected to be reported.
   627  			//
   628  			// TODO(thepudds): consider a test export or similar that lets us more directly see
   629  			// the count of freed objects, reused objects, and allocated objects. This could
   630  			// be in advance of any future runtime/metrics or user-visible API, or perhaps
   631  			// we could introduce the concept of build tag or debug flag controlled runtime/metrics
   632  			// targeting people working on the runtime and compiler (but not formally supported).
   633  			want = 1
   634  		}
   635  
   636  		var r []byte
   637  		got := testing.AllocsPerRun(10, func() {
   638  			r = byteSlice(n)
   639  		})
   640  		if got != float64(want) {
   641  			t.Errorf("for size %d, got %f allocs want %d", n, got, want)
   642  		}
   643  		if cap(r) != wantCap {
   644  			t.Errorf("for size %d, got capacity %d want %d", n, cap(r), wantCap)
   645  		}
   646  	}
   647  }
   648  
   649  //go:noinline
   650  func ptrSlice(n int, p *[]*byte) {
   651  	var r []*byte
   652  	for range n {
   653  		r = append(r, nil)
   654  	}
   655  	*p = r
   656  }
   657  func TestAppendPtrInLoop(t *testing.T) {
   658  	testenv.SkipIfOptimizationOff(t)
   659  	if race.Enabled {
   660  		t.Skip("skipping in -race mode")
   661  	}
   662  	if asan.Enabled || msan.Enabled {
   663  		t.Skip("skipping in sanitizer mode")
   664  	}
   665  	var tests [][3]int
   666  	if runtime.PtrSize == 8 {
   667  		tests = [][3]int{
   668  			{0, 0, 0},
   669  			{1, 1, 1},
   670  			{2, 1, 2},
   671  			{3, 1, 3}, // This is the interesting case, allocates 24 bytes when before it was 32.
   672  			{4, 1, 4},
   673  			{5, 1, 8},
   674  			{6, 1, 8},
   675  			{7, 1, 8},
   676  			{8, 1, 8},
   677  			{9, 2, 16},
   678  		}
   679  	} else {
   680  		tests = [][3]int{
   681  			{0, 0, 0},
   682  			{1, 1, 2},
   683  			{2, 1, 2},
   684  			{3, 1, 4},
   685  			{4, 1, 4},
   686  			{5, 1, 6}, // These two are also 24 bytes instead of 32.
   687  			{6, 1, 6}, //
   688  			{7, 1, 8},
   689  			{8, 1, 8},
   690  			{9, 1, 16},
   691  			{10, 1, 16},
   692  			{11, 1, 16},
   693  			{12, 1, 16},
   694  			{13, 1, 16},
   695  			{14, 1, 16},
   696  			{15, 1, 16},
   697  			{16, 1, 16},
   698  			{17, 2, 32},
   699  		}
   700  	}
   701  	for _, test := range tests {
   702  		n := test[0]
   703  		want := test[1]
   704  		wantCap := test[2]
   705  		var r []*byte
   706  		got := testing.AllocsPerRun(10, func() {
   707  			ptrSlice(n, &r)
   708  		})
   709  		if got != float64(want) {
   710  			t.Errorf("for size %d, got %f allocs want %d", n, got, want)
   711  		}
   712  		if cap(r) != wantCap {
   713  			t.Errorf("for size %d, got capacity %d want %d", n, cap(r), wantCap)
   714  		}
   715  	}
   716  }
   717  
   718  //go:noinline
   719  func byteCapSlice(n int) ([]byte, int) {
   720  	var r []byte
   721  	for i := range n {
   722  		r = append(r, byte(i))
   723  	}
   724  	return r, cap(r)
   725  }
   726  func TestAppendByteCapInLoop(t *testing.T) {
   727  	testenv.SkipIfOptimizationOff(t)
   728  	if race.Enabled {
   729  		t.Skip("skipping in -race mode")
   730  	}
   731  	if asan.Enabled || msan.Enabled {
   732  		t.Skip("skipping in sanitizer mode")
   733  	}
   734  	for _, test := range [][3]int{
   735  		{0, 0, 0},
   736  		{1, 1, 8},
   737  		{2, 1, 8},
   738  		{8, 1, 8},
   739  		{9, 1, 16},
   740  		{16, 1, 16},
   741  		{17, 1, 24},
   742  		{24, 1, 24},
   743  		{25, 1, 32},
   744  		{32, 1, 32},
   745  		{33, 1, 64},
   746  		{48, 1, 64},
   747  		{49, 1, 64},
   748  		{64, 1, 64},
   749  		{65, 2, 128},
   750  	} {
   751  		n := test[0]
   752  		want := test[1]
   753  		wantCap := test[2]
   754  
   755  		if goexperiment.RuntimeFreegc && n > 64 {
   756  			// Only 1 allocation is expected to be reported.
   757  			want = 1
   758  		}
   759  
   760  		var r []byte
   761  		got := testing.AllocsPerRun(10, func() {
   762  			r, _ = byteCapSlice(n)
   763  		})
   764  		if got != float64(want) {
   765  			t.Errorf("for size %d, got %f allocs want %d", n, got, want)
   766  		}
   767  		if cap(r) != wantCap {
   768  			t.Errorf("for size %d, got capacity %d want %d", n, cap(r), wantCap)
   769  		}
   770  	}
   771  }
   772  
   773  func TestAppendGeneric(t *testing.T) {
   774  	type I *int
   775  	r := testAppendGeneric[I](100)
   776  	if len(r) != 100 {
   777  		t.Errorf("bad length")
   778  	}
   779  }
   780  
   781  //go:noinline
   782  func testAppendGeneric[E any](n int) []E {
   783  	var r []E
   784  	var z E
   785  	for range n {
   786  		r = append(r, z)
   787  	}
   788  	return r
   789  }
   790  
   791  func appendSomeBytes(r []byte, s []byte) []byte {
   792  	for _, b := range s {
   793  		r = append(r, b)
   794  	}
   795  	return r
   796  }
   797  
   798  func TestAppendOfArg(t *testing.T) {
   799  	r := make([]byte, 24)
   800  	for i := 0; i < 24; i++ {
   801  		r[i] = byte(i)
   802  	}
   803  	appendSomeBytes(r, []byte{25, 26, 27})
   804  	// Do the same thing, trying to overwrite any
   805  	// stack-allocated buffers used above.
   806  	s := make([]byte, 24)
   807  	for i := 0; i < 24; i++ {
   808  		s[i] = 99
   809  	}
   810  	appendSomeBytes(s, []byte{99, 99, 99})
   811  	// Check that we still have the right data.
   812  	for i, b := range r {
   813  		if b != byte(i) {
   814  			t.Errorf("r[%d]=%d, want %d", i, b, byte(i))
   815  		}
   816  	}
   817  
   818  }
   819  
   820  func BenchmarkAppendInLoop(b *testing.B) {
   821  	for _, size := range []int{0, 1, 8, 16, 32, 64, 128} {
   822  		b.Run(fmt.Sprintf("%d", size),
   823  			func(b *testing.B) {
   824  				b.ReportAllocs()
   825  				for b.Loop() {
   826  					byteSlice(size)
   827  				}
   828  			})
   829  	}
   830  }
   831  
   832  func TestMoveToHeapEarly(t *testing.T) {
   833  	// Just checking that this compiles.
   834  	var x []int
   835  	y := x // causes a move2heap in the entry block
   836  	for range 5 {
   837  		x = append(x, 5)
   838  	}
   839  	_ = y
   840  }
   841  
   842  func TestMoveToHeapCap(t *testing.T) {
   843  	var c int
   844  	r := func() []byte {
   845  		var s []byte
   846  		for i := range 10 {
   847  			s = append(s, byte(i))
   848  		}
   849  		c = cap(s)
   850  		return s
   851  	}()
   852  	if c != cap(r) {
   853  		t.Errorf("got cap=%d, want %d", c, cap(r))
   854  	}
   855  	sinkSlice = r
   856  }
   857  
   858  //go:noinline
   859  func runit(f func()) {
   860  	f()
   861  }
   862  
   863  func TestMoveToHeapClosure1(t *testing.T) {
   864  	var c int
   865  	r := func() []byte {
   866  		var s []byte
   867  		for i := range 10 {
   868  			s = append(s, byte(i))
   869  		}
   870  		runit(func() {
   871  			c = cap(s)
   872  		})
   873  		return s
   874  	}()
   875  	if c != cap(r) {
   876  		t.Errorf("got cap=%d, want %d", c, cap(r))
   877  	}
   878  	sinkSlice = r
   879  }
   880  func TestMoveToHeapClosure2(t *testing.T) {
   881  	var c int
   882  	r := func() []byte {
   883  		var s []byte
   884  		for i := range 10 {
   885  			s = append(s, byte(i))
   886  		}
   887  		c = func() int {
   888  			return cap(s)
   889  		}()
   890  		return s
   891  	}()
   892  	if c != cap(r) {
   893  		t.Errorf("got cap=%d, want %d", c, cap(r))
   894  	}
   895  	sinkSlice = r
   896  }
   897  
   898  //go:noinline
   899  func buildClosure(t *testing.T) ([]byte, func()) {
   900  	var s []byte
   901  	for i := range 20 {
   902  		s = append(s, byte(i))
   903  	}
   904  	c := func() {
   905  		for i, b := range s {
   906  			if b != byte(i) {
   907  				t.Errorf("s[%d]=%d, want %d", i, b, i)
   908  			}
   909  		}
   910  	}
   911  	return s, c
   912  }
   913  
   914  func TestMoveToHeapClosure3(t *testing.T) {
   915  	_, f := buildClosure(t)
   916  	overwriteStack(0)
   917  	f()
   918  }
   919  
   920  //go:noinline
   921  func overwriteStack(n int) uint64 {
   922  	var x [100]uint64
   923  	for i := range x {
   924  		x[i] = 0xabcdabcdabcdabcd
   925  	}
   926  	return x[n]
   927  }
   928  
   929  var sinkSlice []byte
   930  

View as plain text