github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/runtime/malloc_test.go (about)

     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  	"flag"
     9  	"fmt"
    10  	"internal/race"
    11  	"internal/testenv"
    12  	"os"
    13  	"os/exec"
    14  	"reflect"
    15  	. "runtime"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  	"unsafe"
    20  )
    21  
    22  var testMemStatsCount int
    23  
    24  func TestMemStats(t *testing.T) {
    25  	testMemStatsCount++
    26  
    27  	// Make sure there's at least one forced GC.
    28  	GC()
    29  
    30  	// Test that MemStats has sane values.
    31  	st := new(MemStats)
    32  	ReadMemStats(st)
    33  
    34  	nz := func(x interface{}) error {
    35  		if x != reflect.Zero(reflect.TypeOf(x)).Interface() {
    36  			return nil
    37  		}
    38  		return fmt.Errorf("zero value")
    39  	}
    40  	le := func(thresh float64) func(interface{}) error {
    41  		return func(x interface{}) error {
    42  			// These sanity tests aren't necessarily valid
    43  			// with high -test.count values, so only run
    44  			// them once.
    45  			if testMemStatsCount > 1 {
    46  				return nil
    47  			}
    48  
    49  			if reflect.ValueOf(x).Convert(reflect.TypeOf(thresh)).Float() < thresh {
    50  				return nil
    51  			}
    52  			return fmt.Errorf("insanely high value (overflow?); want <= %v", thresh)
    53  		}
    54  	}
    55  	eq := func(x interface{}) func(interface{}) error {
    56  		return func(y interface{}) error {
    57  			if x == y {
    58  				return nil
    59  			}
    60  			return fmt.Errorf("want %v", x)
    61  		}
    62  	}
    63  	// Of the uint fields, HeapReleased, HeapIdle can be 0.
    64  	// PauseTotalNs can be 0 if timer resolution is poor.
    65  	fields := map[string][]func(interface{}) error{
    66  		"Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)},
    67  		"Lookups": {eq(uint64(0))}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)},
    68  		"HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)},
    69  		"HeapInuse": {nz, le(1e10)}, "HeapReleased": {le(1e10)}, "HeapObjects": {nz, le(1e10)},
    70  		"StackInuse": {nz, le(1e10)}, "StackSys": {nz, le(1e10)},
    71  		"MSpanInuse": {nz, le(1e10)}, "MSpanSys": {nz, le(1e10)},
    72  		"MCacheInuse": {nz, le(1e10)}, "MCacheSys": {nz, le(1e10)},
    73  		"BuckHashSys": {nz, le(1e10)}, "GCSys": {nz, le(1e10)}, "OtherSys": {nz, le(1e10)},
    74  		"NextGC": {nz, le(1e10)}, "LastGC": {nz},
    75  		"PauseTotalNs": {le(1e11)}, "PauseNs": nil, "PauseEnd": nil,
    76  		"NumGC": {nz, le(1e9)}, "NumForcedGC": {nz, le(1e9)},
    77  		"GCCPUFraction": {le(0.99)}, "EnableGC": {eq(true)}, "DebugGC": {eq(false)},
    78  		"BySize": nil,
    79  	}
    80  
    81  	rst := reflect.ValueOf(st).Elem()
    82  	for i := 0; i < rst.Type().NumField(); i++ {
    83  		name, val := rst.Type().Field(i).Name, rst.Field(i).Interface()
    84  		checks, ok := fields[name]
    85  		if !ok {
    86  			t.Errorf("unknown MemStats field %s", name)
    87  			continue
    88  		}
    89  		for _, check := range checks {
    90  			if err := check(val); err != nil {
    91  				t.Errorf("%s = %v: %s", name, val, err)
    92  			}
    93  		}
    94  	}
    95  
    96  	if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
    97  		st.BuckHashSys+st.GCSys+st.OtherSys {
    98  		t.Fatalf("Bad sys value: %+v", *st)
    99  	}
   100  
   101  	if st.HeapIdle+st.HeapInuse != st.HeapSys {
   102  		t.Fatalf("HeapIdle(%d) + HeapInuse(%d) should be equal to HeapSys(%d), but isn't.", st.HeapIdle, st.HeapInuse, st.HeapSys)
   103  	}
   104  
   105  	if lpe := st.PauseEnd[int(st.NumGC+255)%len(st.PauseEnd)]; st.LastGC != lpe {
   106  		t.Fatalf("LastGC(%d) != last PauseEnd(%d)", st.LastGC, lpe)
   107  	}
   108  
   109  	var pauseTotal uint64
   110  	for _, pause := range st.PauseNs {
   111  		pauseTotal += pause
   112  	}
   113  	if int(st.NumGC) < len(st.PauseNs) {
   114  		// We have all pauses, so this should be exact.
   115  		if st.PauseTotalNs != pauseTotal {
   116  			t.Fatalf("PauseTotalNs(%d) != sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
   117  		}
   118  		for i := int(st.NumGC); i < len(st.PauseNs); i++ {
   119  			if st.PauseNs[i] != 0 {
   120  				t.Fatalf("Non-zero PauseNs[%d]: %+v", i, st)
   121  			}
   122  			if st.PauseEnd[i] != 0 {
   123  				t.Fatalf("Non-zero PauseEnd[%d]: %+v", i, st)
   124  			}
   125  		}
   126  	} else {
   127  		if st.PauseTotalNs < pauseTotal {
   128  			t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
   129  		}
   130  	}
   131  
   132  	if st.NumForcedGC > st.NumGC {
   133  		t.Fatalf("NumForcedGC(%d) > NumGC(%d)", st.NumForcedGC, st.NumGC)
   134  	}
   135  }
   136  
   137  func TestStringConcatenationAllocs(t *testing.T) {
   138  	n := testing.AllocsPerRun(1e3, func() {
   139  		b := make([]byte, 10)
   140  		for i := 0; i < 10; i++ {
   141  			b[i] = byte(i) + '0'
   142  		}
   143  		s := "foo" + string(b)
   144  		if want := "foo0123456789"; s != want {
   145  			t.Fatalf("want %v, got %v", want, s)
   146  		}
   147  	})
   148  	// Only string concatenation allocates.
   149  	if n != 1 {
   150  		t.Fatalf("want 1 allocation, got %v", n)
   151  	}
   152  }
   153  
   154  func TestTinyAlloc(t *testing.T) {
   155  	const N = 16
   156  	var v [N]unsafe.Pointer
   157  	for i := range v {
   158  		v[i] = unsafe.Pointer(new(byte))
   159  	}
   160  
   161  	chunks := make(map[uintptr]bool, N)
   162  	for _, p := range v {
   163  		chunks[uintptr(p)&^7] = true
   164  	}
   165  
   166  	if len(chunks) == N {
   167  		t.Fatal("no bytes allocated within the same 8-byte chunk")
   168  	}
   169  }
   170  
   171  func TestPhysicalMemoryUtilization(t *testing.T) {
   172  	got := runTestProg(t, "testprog", "GCPhys")
   173  	want := "OK\n"
   174  	if got != want {
   175  		t.Fatalf("expected %q, but got %q", want, got)
   176  	}
   177  }
   178  
   179  type acLink struct {
   180  	x [1 << 20]byte
   181  }
   182  
   183  var arenaCollisionSink []*acLink
   184  
   185  func TestArenaCollision(t *testing.T) {
   186  	if GOOS == "darwin" && race.Enabled {
   187  		// Skip this test on Darwin in race mode because Darwin 10.10 has
   188  		// issues following arena hints and runs out of them in race mode, so
   189  		// MAP_FIXED is used to ensure we keep the heap in the memory region the
   190  		// race detector expects.
   191  		// TODO(mknyszek): Delete this when Darwin 10.10 is no longer supported.
   192  		t.Skip("disabled on Darwin with race mode since MAP_FIXED is used")
   193  	}
   194  	testenv.MustHaveExec(t)
   195  
   196  	// Test that mheap.sysAlloc handles collisions with other
   197  	// memory mappings.
   198  	if os.Getenv("TEST_ARENA_COLLISION") != "1" {
   199  		cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestArenaCollision", "-test.v"))
   200  		cmd.Env = append(cmd.Env, "TEST_ARENA_COLLISION=1")
   201  		out, err := cmd.CombinedOutput()
   202  		if race.Enabled {
   203  			// This test runs the runtime out of hint
   204  			// addresses, so it will start mapping the
   205  			// heap wherever it can. The race detector
   206  			// doesn't support this, so look for the
   207  			// expected failure.
   208  			if want := "too many address space collisions"; !strings.Contains(string(out), want) {
   209  				t.Fatalf("want %q, got:\n%s", want, string(out))
   210  			}
   211  		} else if !strings.Contains(string(out), "PASS\n") || err != nil {
   212  			t.Fatalf("%s\n(exit status %v)", string(out), err)
   213  		}
   214  		return
   215  	}
   216  	disallowed := [][2]uintptr{}
   217  	// Drop all but the next 3 hints. 64-bit has a lot of hints,
   218  	// so it would take a lot of memory to go through all of them.
   219  	KeepNArenaHints(3)
   220  	// Consume these 3 hints and force the runtime to find some
   221  	// fallback hints.
   222  	for i := 0; i < 5; i++ {
   223  		// Reserve memory at the next hint so it can't be used
   224  		// for the heap.
   225  		start, end := MapNextArenaHint()
   226  		disallowed = append(disallowed, [2]uintptr{start, end})
   227  		// Allocate until the runtime tries to use the hint we
   228  		// just mapped over.
   229  		hint := GetNextArenaHint()
   230  		for GetNextArenaHint() == hint {
   231  			ac := new(acLink)
   232  			arenaCollisionSink = append(arenaCollisionSink, ac)
   233  			// The allocation must not have fallen into
   234  			// one of the reserved regions.
   235  			p := uintptr(unsafe.Pointer(ac))
   236  			for _, d := range disallowed {
   237  				if d[0] <= p && p < d[1] {
   238  					t.Fatalf("allocation %#x in reserved region [%#x, %#x)", p, d[0], d[1])
   239  				}
   240  			}
   241  		}
   242  	}
   243  }
   244  
   245  var mallocSink uintptr
   246  
   247  func BenchmarkMalloc8(b *testing.B) {
   248  	var x uintptr
   249  	for i := 0; i < b.N; i++ {
   250  		p := new(int64)
   251  		x ^= uintptr(unsafe.Pointer(p))
   252  	}
   253  	mallocSink = x
   254  }
   255  
   256  func BenchmarkMalloc16(b *testing.B) {
   257  	var x uintptr
   258  	for i := 0; i < b.N; i++ {
   259  		p := new([2]int64)
   260  		x ^= uintptr(unsafe.Pointer(p))
   261  	}
   262  	mallocSink = x
   263  }
   264  
   265  func BenchmarkMallocTypeInfo8(b *testing.B) {
   266  	var x uintptr
   267  	for i := 0; i < b.N; i++ {
   268  		p := new(struct {
   269  			p [8 / unsafe.Sizeof(uintptr(0))]*int
   270  		})
   271  		x ^= uintptr(unsafe.Pointer(p))
   272  	}
   273  	mallocSink = x
   274  }
   275  
   276  func BenchmarkMallocTypeInfo16(b *testing.B) {
   277  	var x uintptr
   278  	for i := 0; i < b.N; i++ {
   279  		p := new(struct {
   280  			p [16 / unsafe.Sizeof(uintptr(0))]*int
   281  		})
   282  		x ^= uintptr(unsafe.Pointer(p))
   283  	}
   284  	mallocSink = x
   285  }
   286  
   287  type LargeStruct struct {
   288  	x [16][]byte
   289  }
   290  
   291  func BenchmarkMallocLargeStruct(b *testing.B) {
   292  	var x uintptr
   293  	for i := 0; i < b.N; i++ {
   294  		p := make([]LargeStruct, 2)
   295  		x ^= uintptr(unsafe.Pointer(&p[0]))
   296  	}
   297  	mallocSink = x
   298  }
   299  
   300  var n = flag.Int("n", 1000, "number of goroutines")
   301  
   302  func BenchmarkGoroutineSelect(b *testing.B) {
   303  	quit := make(chan struct{})
   304  	read := func(ch chan struct{}) {
   305  		for {
   306  			select {
   307  			case _, ok := <-ch:
   308  				if !ok {
   309  					return
   310  				}
   311  			case <-quit:
   312  				return
   313  			}
   314  		}
   315  	}
   316  	benchHelper(b, *n, read)
   317  }
   318  
   319  func BenchmarkGoroutineBlocking(b *testing.B) {
   320  	read := func(ch chan struct{}) {
   321  		for {
   322  			if _, ok := <-ch; !ok {
   323  				return
   324  			}
   325  		}
   326  	}
   327  	benchHelper(b, *n, read)
   328  }
   329  
   330  func BenchmarkGoroutineForRange(b *testing.B) {
   331  	read := func(ch chan struct{}) {
   332  		for range ch {
   333  		}
   334  	}
   335  	benchHelper(b, *n, read)
   336  }
   337  
   338  func benchHelper(b *testing.B, n int, read func(chan struct{})) {
   339  	m := make([]chan struct{}, n)
   340  	for i := range m {
   341  		m[i] = make(chan struct{}, 1)
   342  		go read(m[i])
   343  	}
   344  	b.StopTimer()
   345  	b.ResetTimer()
   346  	GC()
   347  
   348  	for i := 0; i < b.N; i++ {
   349  		for _, ch := range m {
   350  			if ch != nil {
   351  				ch <- struct{}{}
   352  			}
   353  		}
   354  		time.Sleep(10 * time.Millisecond)
   355  		b.StartTimer()
   356  		GC()
   357  		b.StopTimer()
   358  	}
   359  
   360  	for _, ch := range m {
   361  		close(ch)
   362  	}
   363  	time.Sleep(10 * time.Millisecond)
   364  }
   365  
   366  func BenchmarkGoroutineIdle(b *testing.B) {
   367  	quit := make(chan struct{})
   368  	fn := func() {
   369  		<-quit
   370  	}
   371  	for i := 0; i < *n; i++ {
   372  		go fn()
   373  	}
   374  
   375  	GC()
   376  	b.ResetTimer()
   377  
   378  	for i := 0; i < b.N; i++ {
   379  		GC()
   380  	}
   381  
   382  	b.StopTimer()
   383  	close(quit)
   384  	time.Sleep(10 * time.Millisecond)
   385  }