Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/memmove_test.go

Documentation: runtime

     1  // Copyright 2013 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  	"crypto/rand"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"internal/race"
    12  	"internal/testenv"
    13  	. "runtime"
    14  	"sync/atomic"
    15  	"testing"
    16  	"unsafe"
    17  )
    18  
    19  func TestMemmove(t *testing.T) {
    20  	if *flagQuick {
    21  		t.Skip("-quick")
    22  	}
    23  	t.Parallel()
    24  	size := 256
    25  	if testing.Short() {
    26  		size = 128 + 16
    27  	}
    28  	src := make([]byte, size)
    29  	dst := make([]byte, size)
    30  	for i := 0; i < size; i++ {
    31  		src[i] = byte(128 + (i & 127))
    32  	}
    33  	for i := 0; i < size; i++ {
    34  		dst[i] = byte(i & 127)
    35  	}
    36  	for n := 0; n <= size; n++ {
    37  		for x := 0; x <= size-n; x++ { // offset in src
    38  			for y := 0; y <= size-n; y++ { // offset in dst
    39  				copy(dst[y:y+n], src[x:x+n])
    40  				for i := 0; i < y; i++ {
    41  					if dst[i] != byte(i&127) {
    42  						t.Fatalf("prefix dst[%d] = %d", i, dst[i])
    43  					}
    44  				}
    45  				for i := y; i < y+n; i++ {
    46  					if dst[i] != byte(128+((i-y+x)&127)) {
    47  						t.Fatalf("copied dst[%d] = %d", i, dst[i])
    48  					}
    49  					dst[i] = byte(i & 127) // reset dst
    50  				}
    51  				for i := y + n; i < size; i++ {
    52  					if dst[i] != byte(i&127) {
    53  						t.Fatalf("suffix dst[%d] = %d", i, dst[i])
    54  					}
    55  				}
    56  			}
    57  		}
    58  	}
    59  }
    60  
    61  func TestMemmoveAlias(t *testing.T) {
    62  	if *flagQuick {
    63  		t.Skip("-quick")
    64  	}
    65  	t.Parallel()
    66  	size := 256
    67  	if testing.Short() {
    68  		size = 128 + 16
    69  	}
    70  	buf := make([]byte, size)
    71  	for i := 0; i < size; i++ {
    72  		buf[i] = byte(i)
    73  	}
    74  	for n := 0; n <= size; n++ {
    75  		for x := 0; x <= size-n; x++ { // src offset
    76  			for y := 0; y <= size-n; y++ { // dst offset
    77  				copy(buf[y:y+n], buf[x:x+n])
    78  				for i := 0; i < y; i++ {
    79  					if buf[i] != byte(i) {
    80  						t.Fatalf("prefix buf[%d] = %d", i, buf[i])
    81  					}
    82  				}
    83  				for i := y; i < y+n; i++ {
    84  					if buf[i] != byte(i-y+x) {
    85  						t.Fatalf("copied buf[%d] = %d", i, buf[i])
    86  					}
    87  					buf[i] = byte(i) // reset buf
    88  				}
    89  				for i := y + n; i < size; i++ {
    90  					if buf[i] != byte(i) {
    91  						t.Fatalf("suffix buf[%d] = %d", i, buf[i])
    92  					}
    93  				}
    94  			}
    95  		}
    96  	}
    97  }
    98  
    99  func TestMemmoveLarge0x180000(t *testing.T) {
   100  	if testing.Short() && testenv.Builder() == "" {
   101  		t.Skip("-short")
   102  	}
   103  
   104  	t.Parallel()
   105  	if race.Enabled {
   106  		t.Skip("skipping large memmove test under race detector")
   107  	}
   108  	testSize(t, 0x180000)
   109  }
   110  
   111  func TestMemmoveOverlapLarge0x120000(t *testing.T) {
   112  	if testing.Short() && testenv.Builder() == "" {
   113  		t.Skip("-short")
   114  	}
   115  
   116  	t.Parallel()
   117  	if race.Enabled {
   118  		t.Skip("skipping large memmove test under race detector")
   119  	}
   120  	testOverlap(t, 0x120000)
   121  }
   122  
   123  func testSize(t *testing.T, size int) {
   124  	src := make([]byte, size)
   125  	dst := make([]byte, size)
   126  	_, _ = rand.Read(src)
   127  	_, _ = rand.Read(dst)
   128  
   129  	ref := make([]byte, size)
   130  	copyref(ref, dst)
   131  
   132  	for n := size - 50; n > 1; n >>= 1 {
   133  		for x := 0; x <= size-n; x = x*7 + 1 { // offset in src
   134  			for y := 0; y <= size-n; y = y*9 + 1 { // offset in dst
   135  				copy(dst[y:y+n], src[x:x+n])
   136  				copyref(ref[y:y+n], src[x:x+n])
   137  				p := cmpb(dst, ref)
   138  				if p >= 0 {
   139  					t.Fatalf("Copy failed, copying from src[%d:%d] to dst[%d:%d].\nOffset %d is different, %v != %v", x, x+n, y, y+n, p, dst[p], ref[p])
   140  				}
   141  			}
   142  		}
   143  	}
   144  }
   145  
   146  func testOverlap(t *testing.T, size int) {
   147  	src := make([]byte, size)
   148  	test := make([]byte, size)
   149  	ref := make([]byte, size)
   150  	_, _ = rand.Read(src)
   151  
   152  	for n := size - 50; n > 1; n >>= 1 {
   153  		for x := 0; x <= size-n; x = x*7 + 1 { // offset in src
   154  			for y := 0; y <= size-n; y = y*9 + 1 { // offset in dst
   155  				// Reset input
   156  				copyref(test, src)
   157  				copyref(ref, src)
   158  				copy(test[y:y+n], test[x:x+n])
   159  				if y <= x {
   160  					copyref(ref[y:y+n], ref[x:x+n])
   161  				} else {
   162  					copybw(ref[y:y+n], ref[x:x+n])
   163  				}
   164  				p := cmpb(test, ref)
   165  				if p >= 0 {
   166  					t.Fatalf("Copy failed, copying from src[%d:%d] to dst[%d:%d].\nOffset %d is different, %v != %v", x, x+n, y, y+n, p, test[p], ref[p])
   167  				}
   168  			}
   169  		}
   170  	}
   171  
   172  }
   173  
   174  // Forward copy.
   175  func copyref(dst, src []byte) {
   176  	for i, v := range src {
   177  		dst[i] = v
   178  	}
   179  }
   180  
   181  // Backwards copy
   182  func copybw(dst, src []byte) {
   183  	if len(src) == 0 {
   184  		return
   185  	}
   186  	for i := len(src) - 1; i >= 0; i-- {
   187  		dst[i] = src[i]
   188  	}
   189  }
   190  
   191  // Returns offset of difference
   192  func matchLen(a, b []byte, max int) int {
   193  	a = a[:max]
   194  	b = b[:max]
   195  	for i, av := range a {
   196  		if b[i] != av {
   197  			return i
   198  		}
   199  	}
   200  	return max
   201  }
   202  
   203  func cmpb(a, b []byte) int {
   204  	l := matchLen(a, b, len(a))
   205  	if l == len(a) {
   206  		return -1
   207  	}
   208  	return l
   209  }
   210  
   211  // Ensure that memmove writes pointers atomically, so the GC won't
   212  // observe a partially updated pointer.
   213  func TestMemmoveAtomicity(t *testing.T) {
   214  	if race.Enabled {
   215  		t.Skip("skip under the race detector -- this test is intentionally racy")
   216  	}
   217  
   218  	var x int
   219  
   220  	for _, backward := range []bool{true, false} {
   221  		for _, n := range []int{3, 4, 5, 6, 7, 8, 9, 10, 15, 25, 49} {
   222  			n := n
   223  
   224  			// test copying [N]*int.
   225  			sz := uintptr(n * PtrSize)
   226  			name := fmt.Sprint(sz)
   227  			if backward {
   228  				name += "-backward"
   229  			} else {
   230  				name += "-forward"
   231  			}
   232  			t.Run(name, func(t *testing.T) {
   233  				// Use overlapping src and dst to force forward/backward copy.
   234  				var s [100]*int
   235  				src := s[n-1 : 2*n-1]
   236  				dst := s[:n]
   237  				if backward {
   238  					src, dst = dst, src
   239  				}
   240  				for i := range src {
   241  					src[i] = &x
   242  				}
   243  				for i := range dst {
   244  					dst[i] = nil
   245  				}
   246  
   247  				var ready uint32
   248  				go func() {
   249  					sp := unsafe.Pointer(&src[0])
   250  					dp := unsafe.Pointer(&dst[0])
   251  					atomic.StoreUint32(&ready, 1)
   252  					for i := 0; i < 10000; i++ {
   253  						Memmove(dp, sp, sz)
   254  						MemclrNoHeapPointers(dp, sz)
   255  					}
   256  					atomic.StoreUint32(&ready, 2)
   257  				}()
   258  
   259  				for atomic.LoadUint32(&ready) == 0 {
   260  					Gosched()
   261  				}
   262  
   263  				for atomic.LoadUint32(&ready) != 2 {
   264  					for i := range dst {
   265  						p := dst[i]
   266  						if p != nil && p != &x {
   267  							t.Fatalf("got partially updated pointer %p at dst[%d], want either nil or %p", p, i, &x)
   268  						}
   269  					}
   270  				}
   271  			})
   272  		}
   273  	}
   274  }
   275  
   276  func benchmarkSizes(b *testing.B, sizes []int, fn func(b *testing.B, n int)) {
   277  	for _, n := range sizes {
   278  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   279  			b.SetBytes(int64(n))
   280  			fn(b, n)
   281  		})
   282  	}
   283  }
   284  
   285  var bufSizes = []int{
   286  	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
   287  	32, 64, 128, 256, 512, 1024, 2048, 4096,
   288  }
   289  var bufSizesOverlap = []int{
   290  	32, 64, 128, 256, 512, 1024, 2048, 4096,
   291  }
   292  
   293  func BenchmarkMemmove(b *testing.B) {
   294  	benchmarkSizes(b, bufSizes, func(b *testing.B, n int) {
   295  		x := make([]byte, n)
   296  		y := make([]byte, n)
   297  		for i := 0; i < b.N; i++ {
   298  			copy(x, y)
   299  		}
   300  	})
   301  }
   302  
   303  func BenchmarkMemmoveOverlap(b *testing.B) {
   304  	benchmarkSizes(b, bufSizesOverlap, func(b *testing.B, n int) {
   305  		x := make([]byte, n+16)
   306  		for i := 0; i < b.N; i++ {
   307  			copy(x[16:n+16], x[:n])
   308  		}
   309  	})
   310  }
   311  
   312  func BenchmarkMemmoveUnalignedDst(b *testing.B) {
   313  	benchmarkSizes(b, bufSizes, func(b *testing.B, n int) {
   314  		x := make([]byte, n+1)
   315  		y := make([]byte, n)
   316  		for i := 0; i < b.N; i++ {
   317  			copy(x[1:], y)
   318  		}
   319  	})
   320  }
   321  
   322  func BenchmarkMemmoveUnalignedDstOverlap(b *testing.B) {
   323  	benchmarkSizes(b, bufSizesOverlap, func(b *testing.B, n int) {
   324  		x := make([]byte, n+16)
   325  		for i := 0; i < b.N; i++ {
   326  			copy(x[16:n+16], x[1:n+1])
   327  		}
   328  	})
   329  }
   330  
   331  func BenchmarkMemmoveUnalignedSrc(b *testing.B) {
   332  	benchmarkSizes(b, bufSizes, func(b *testing.B, n int) {
   333  		x := make([]byte, n)
   334  		y := make([]byte, n+1)
   335  		for i := 0; i < b.N; i++ {
   336  			copy(x, y[1:])
   337  		}
   338  	})
   339  }
   340  
   341  func BenchmarkMemmoveUnalignedSrcOverlap(b *testing.B) {
   342  	benchmarkSizes(b, bufSizesOverlap, func(b *testing.B, n int) {
   343  		x := make([]byte, n+1)
   344  		for i := 0; i < b.N; i++ {
   345  			copy(x[1:n+1], x[:n])
   346  		}
   347  	})
   348  }
   349  
   350  func TestMemclr(t *testing.T) {
   351  	size := 512
   352  	if testing.Short() {
   353  		size = 128 + 16
   354  	}
   355  	mem := make([]byte, size)
   356  	for i := 0; i < size; i++ {
   357  		mem[i] = 0xee
   358  	}
   359  	for n := 0; n < size; n++ {
   360  		for x := 0; x <= size-n; x++ { // offset in mem
   361  			MemclrBytes(mem[x : x+n])
   362  			for i := 0; i < x; i++ {
   363  				if mem[i] != 0xee {
   364  					t.Fatalf("overwrite prefix mem[%d] = %d", i, mem[i])
   365  				}
   366  			}
   367  			for i := x; i < x+n; i++ {
   368  				if mem[i] != 0 {
   369  					t.Fatalf("failed clear mem[%d] = %d", i, mem[i])
   370  				}
   371  				mem[i] = 0xee
   372  			}
   373  			for i := x + n; i < size; i++ {
   374  				if mem[i] != 0xee {
   375  					t.Fatalf("overwrite suffix mem[%d] = %d", i, mem[i])
   376  				}
   377  			}
   378  		}
   379  	}
   380  }
   381  
   382  func BenchmarkMemclr(b *testing.B) {
   383  	for _, n := range []int{5, 16, 64, 256, 4096, 65536} {
   384  		x := make([]byte, n)
   385  		b.Run(fmt.Sprint(n), func(b *testing.B) {
   386  			b.SetBytes(int64(n))
   387  			for i := 0; i < b.N; i++ {
   388  				MemclrBytes(x)
   389  			}
   390  		})
   391  	}
   392  	for _, m := range []int{1, 4, 8, 16, 64} {
   393  		x := make([]byte, m<<20)
   394  		b.Run(fmt.Sprint(m, "M"), func(b *testing.B) {
   395  			b.SetBytes(int64(m << 20))
   396  			for i := 0; i < b.N; i++ {
   397  				MemclrBytes(x)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func BenchmarkGoMemclr(b *testing.B) {
   404  	benchmarkSizes(b, []int{5, 16, 64, 256}, func(b *testing.B, n int) {
   405  		x := make([]byte, n)
   406  		for i := 0; i < b.N; i++ {
   407  			for j := range x {
   408  				x[j] = 0
   409  			}
   410  		}
   411  	})
   412  }
   413  
   414  func BenchmarkClearFat8(b *testing.B) {
   415  	for i := 0; i < b.N; i++ {
   416  		var x [8 / 4]uint32
   417  		_ = x
   418  	}
   419  }
   420  func BenchmarkClearFat12(b *testing.B) {
   421  	for i := 0; i < b.N; i++ {
   422  		var x [12 / 4]uint32
   423  		_ = x
   424  	}
   425  }
   426  func BenchmarkClearFat16(b *testing.B) {
   427  	for i := 0; i < b.N; i++ {
   428  		var x [16 / 4]uint32
   429  		_ = x
   430  	}
   431  }
   432  func BenchmarkClearFat24(b *testing.B) {
   433  	for i := 0; i < b.N; i++ {
   434  		var x [24 / 4]uint32
   435  		_ = x
   436  	}
   437  }
   438  func BenchmarkClearFat32(b *testing.B) {
   439  	for i := 0; i < b.N; i++ {
   440  		var x [32 / 4]uint32
   441  		_ = x
   442  	}
   443  }
   444  func BenchmarkClearFat40(b *testing.B) {
   445  	for i := 0; i < b.N; i++ {
   446  		var x [40 / 4]uint32
   447  		_ = x
   448  	}
   449  }
   450  func BenchmarkClearFat48(b *testing.B) {
   451  	for i := 0; i < b.N; i++ {
   452  		var x [48 / 4]uint32
   453  		_ = x
   454  	}
   455  }
   456  func BenchmarkClearFat56(b *testing.B) {
   457  	for i := 0; i < b.N; i++ {
   458  		var x [56 / 4]uint32
   459  		_ = x
   460  	}
   461  }
   462  func BenchmarkClearFat64(b *testing.B) {
   463  	for i := 0; i < b.N; i++ {
   464  		var x [64 / 4]uint32
   465  		_ = x
   466  	}
   467  }
   468  func BenchmarkClearFat128(b *testing.B) {
   469  	for i := 0; i < b.N; i++ {
   470  		var x [128 / 4]uint32
   471  		_ = x
   472  	}
   473  }
   474  func BenchmarkClearFat256(b *testing.B) {
   475  	for i := 0; i < b.N; i++ {
   476  		var x [256 / 4]uint32
   477  		_ = x
   478  	}
   479  }
   480  func BenchmarkClearFat512(b *testing.B) {
   481  	for i := 0; i < b.N; i++ {
   482  		var x [512 / 4]uint32
   483  		_ = x
   484  	}
   485  }
   486  func BenchmarkClearFat1024(b *testing.B) {
   487  	for i := 0; i < b.N; i++ {
   488  		var x [1024 / 4]uint32
   489  		_ = x
   490  	}
   491  }
   492  
   493  func BenchmarkCopyFat8(b *testing.B) {
   494  	var x [8 / 4]uint32
   495  	for i := 0; i < b.N; i++ {
   496  		y := x
   497  		_ = y
   498  	}
   499  }
   500  func BenchmarkCopyFat12(b *testing.B) {
   501  	var x [12 / 4]uint32
   502  	for i := 0; i < b.N; i++ {
   503  		y := x
   504  		_ = y
   505  	}
   506  }
   507  func BenchmarkCopyFat16(b *testing.B) {
   508  	var x [16 / 4]uint32
   509  	for i := 0; i < b.N; i++ {
   510  		y := x
   511  		_ = y
   512  	}
   513  }
   514  func BenchmarkCopyFat24(b *testing.B) {
   515  	var x [24 / 4]uint32
   516  	for i := 0; i < b.N; i++ {
   517  		y := x
   518  		_ = y
   519  	}
   520  }
   521  func BenchmarkCopyFat32(b *testing.B) {
   522  	var x [32 / 4]uint32
   523  	for i := 0; i < b.N; i++ {
   524  		y := x
   525  		_ = y
   526  	}
   527  }
   528  func BenchmarkCopyFat64(b *testing.B) {
   529  	var x [64 / 4]uint32
   530  	for i := 0; i < b.N; i++ {
   531  		y := x
   532  		_ = y
   533  	}
   534  }
   535  func BenchmarkCopyFat128(b *testing.B) {
   536  	var x [128 / 4]uint32
   537  	for i := 0; i < b.N; i++ {
   538  		y := x
   539  		_ = y
   540  	}
   541  }
   542  func BenchmarkCopyFat256(b *testing.B) {
   543  	var x [256 / 4]uint32
   544  	for i := 0; i < b.N; i++ {
   545  		y := x
   546  		_ = y
   547  	}
   548  }
   549  func BenchmarkCopyFat512(b *testing.B) {
   550  	var x [512 / 4]uint32
   551  	for i := 0; i < b.N; i++ {
   552  		y := x
   553  		_ = y
   554  	}
   555  }
   556  func BenchmarkCopyFat520(b *testing.B) {
   557  	var x [520 / 4]uint32
   558  	for i := 0; i < b.N; i++ {
   559  		y := x
   560  		_ = y
   561  	}
   562  }
   563  func BenchmarkCopyFat1024(b *testing.B) {
   564  	var x [1024 / 4]uint32
   565  	for i := 0; i < b.N; i++ {
   566  		y := x
   567  		_ = y
   568  	}
   569  }
   570  
   571  // BenchmarkIssue18740 ensures that memmove uses 4 and 8 byte load/store to move 4 and 8 bytes.
   572  // It used to do 2 2-byte load/stores, which leads to a pipeline stall
   573  // when we try to read the result with one 4-byte load.
   574  func BenchmarkIssue18740(b *testing.B) {
   575  	benchmarks := []struct {
   576  		name  string
   577  		nbyte int
   578  		f     func([]byte) uint64
   579  	}{
   580  		{"2byte", 2, func(buf []byte) uint64 { return uint64(binary.LittleEndian.Uint16(buf)) }},
   581  		{"4byte", 4, func(buf []byte) uint64 { return uint64(binary.LittleEndian.Uint32(buf)) }},
   582  		{"8byte", 8, func(buf []byte) uint64 { return binary.LittleEndian.Uint64(buf) }},
   583  	}
   584  
   585  	var g [4096]byte
   586  	for _, bm := range benchmarks {
   587  		buf := make([]byte, bm.nbyte)
   588  		b.Run(bm.name, func(b *testing.B) {
   589  			for j := 0; j < b.N; j++ {
   590  				for i := 0; i < 4096; i += bm.nbyte {
   591  					copy(buf[:], g[i:])
   592  					sink += bm.f(buf[:])
   593  				}
   594  			}
   595  		})
   596  	}
   597  }
   598  

View as plain text