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  	"testing"
    10  )
    11  
    12  const N = 20
    13  
    14  func 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  
    92  type (
    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  
    98  func 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  
   154  func 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  
   202  var (
   203  	SinkIntSlice        []int
   204  	SinkIntPointerSlice []*int
   205  )
   206  
   207  func 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  
   232  func 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  
   244  func 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  
   253  func 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  
   263  func 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  
   276  var (
   277  	blackhole []byte
   278  )
   279  
   280  func 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  
   292  func 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  
   310  func 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  
   327  var x []int
   328  
   329  func f() int {
   330  	x[:1][0] = 3
   331  	return 2
   332  }
   333  
   334  func 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  
   342  func 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  
   352  func 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  
   374  var (
   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.
   396  func 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  

View as plain text