github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/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  	. "runtime"
    10  	"testing"
    11  	"time"
    12  	"unsafe"
    13  )
    14  
    15  func TestMemStats(t *testing.T) {
    16  	// Test that MemStats has sane values.
    17  	st := new(MemStats)
    18  	ReadMemStats(st)
    19  
    20  	// Everything except HeapReleased, HeapIdle, and NumGC,
    21  	// because they indeed can be 0.
    22  	if st.Alloc == 0 || st.TotalAlloc == 0 || st.Sys == 0 || st.Lookups == 0 ||
    23  		st.Mallocs == 0 || st.Frees == 0 || st.HeapAlloc == 0 || st.HeapSys == 0 ||
    24  		st.HeapInuse == 0 || st.HeapObjects == 0 || st.StackInuse == 0 ||
    25  		st.StackSys == 0 || st.MSpanInuse == 0 || st.MSpanSys == 0 || st.MCacheInuse == 0 ||
    26  		st.MCacheSys == 0 || st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 ||
    27  		st.NextGC == 0 {
    28  		t.Fatalf("Zero value: %+v", *st)
    29  	}
    30  
    31  	if st.Alloc > 1e10 || st.TotalAlloc > 1e11 || st.Sys > 1e10 || st.Lookups > 1e10 ||
    32  		st.Mallocs > 1e10 || st.Frees > 1e10 || st.HeapAlloc > 1e10 || st.HeapSys > 1e10 ||
    33  		st.HeapIdle > 1e10 || st.HeapInuse > 1e10 || st.HeapObjects > 1e10 || st.StackInuse > 1e10 ||
    34  		st.StackSys > 1e10 || st.MSpanInuse > 1e10 || st.MSpanSys > 1e10 || st.MCacheInuse > 1e10 ||
    35  		st.MCacheSys > 1e10 || st.BuckHashSys > 1e10 || st.GCSys > 1e10 || st.OtherSys > 1e10 ||
    36  		st.NextGC > 1e10 || st.NumGC > 1e9 || st.PauseTotalNs > 1e11 {
    37  		t.Fatalf("Insanely high value (overflow?): %+v", *st)
    38  	}
    39  
    40  	if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
    41  		st.BuckHashSys+st.GCSys+st.OtherSys {
    42  		t.Fatalf("Bad sys value: %+v", *st)
    43  	}
    44  
    45  	if st.HeapIdle+st.HeapInuse != st.HeapSys {
    46  		t.Fatalf("HeapIdle(%d) + HeapInuse(%d) should be equal to HeapSys(%d), but isn't.", st.HeapIdle, st.HeapInuse, st.HeapSys)
    47  	}
    48  
    49  	if lpe := st.PauseEnd[int(st.NumGC+255)%len(st.PauseEnd)]; st.LastGC != lpe {
    50  		t.Fatalf("LastGC(%d) != last PauseEnd(%d)", st.LastGC, lpe)
    51  	}
    52  
    53  	var pauseTotal uint64
    54  	for _, pause := range st.PauseNs {
    55  		pauseTotal += pause
    56  	}
    57  	if int(st.NumGC) < len(st.PauseNs) {
    58  		// We have all pauses, so this should be exact.
    59  		if st.PauseTotalNs != pauseTotal {
    60  			t.Fatalf("PauseTotalNs(%d) != sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
    61  		}
    62  		for i := int(st.NumGC); i < len(st.PauseNs); i++ {
    63  			if st.PauseNs[i] != 0 {
    64  				t.Fatalf("Non-zero PauseNs[%d]: %+v", i, st)
    65  			}
    66  			if st.PauseEnd[i] != 0 {
    67  				t.Fatalf("Non-zero PauseEnd[%d]: %+v", i, st)
    68  			}
    69  		}
    70  	} else {
    71  		if st.PauseTotalNs < pauseTotal {
    72  			t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
    73  		}
    74  	}
    75  }
    76  
    77  func TestStringConcatenationAllocs(t *testing.T) {
    78  	n := testing.AllocsPerRun(1e3, func() {
    79  		b := make([]byte, 10)
    80  		for i := 0; i < 10; i++ {
    81  			b[i] = byte(i) + '0'
    82  		}
    83  		s := "foo" + string(b)
    84  		if want := "foo0123456789"; s != want {
    85  			t.Fatalf("want %v, got %v", want, s)
    86  		}
    87  	})
    88  	// Only string concatenation allocates.
    89  	if n != 1 {
    90  		t.Fatalf("want 1 allocation, got %v", n)
    91  	}
    92  }
    93  
    94  func TestTinyAlloc(t *testing.T) {
    95  	const N = 16
    96  	var v [N]unsafe.Pointer
    97  	for i := range v {
    98  		v[i] = unsafe.Pointer(new(byte))
    99  	}
   100  
   101  	chunks := make(map[uintptr]bool, N)
   102  	for _, p := range v {
   103  		chunks[uintptr(p)&^7] = true
   104  	}
   105  
   106  	if len(chunks) == N {
   107  		t.Fatal("no bytes allocated within the same 8-byte chunk")
   108  	}
   109  }
   110  
   111  var mallocSink uintptr
   112  
   113  func BenchmarkMalloc8(b *testing.B) {
   114  	var x uintptr
   115  	for i := 0; i < b.N; i++ {
   116  		p := new(int64)
   117  		x ^= uintptr(unsafe.Pointer(p))
   118  	}
   119  	mallocSink = x
   120  }
   121  
   122  func BenchmarkMalloc16(b *testing.B) {
   123  	var x uintptr
   124  	for i := 0; i < b.N; i++ {
   125  		p := new([2]int64)
   126  		x ^= uintptr(unsafe.Pointer(p))
   127  	}
   128  	mallocSink = x
   129  }
   130  
   131  func BenchmarkMallocTypeInfo8(b *testing.B) {
   132  	var x uintptr
   133  	for i := 0; i < b.N; i++ {
   134  		p := new(struct {
   135  			p [8 / unsafe.Sizeof(uintptr(0))]*int
   136  		})
   137  		x ^= uintptr(unsafe.Pointer(p))
   138  	}
   139  	mallocSink = x
   140  }
   141  
   142  func BenchmarkMallocTypeInfo16(b *testing.B) {
   143  	var x uintptr
   144  	for i := 0; i < b.N; i++ {
   145  		p := new(struct {
   146  			p [16 / unsafe.Sizeof(uintptr(0))]*int
   147  		})
   148  		x ^= uintptr(unsafe.Pointer(p))
   149  	}
   150  	mallocSink = x
   151  }
   152  
   153  type LargeStruct struct {
   154  	x [16][]byte
   155  }
   156  
   157  func BenchmarkMallocLargeStruct(b *testing.B) {
   158  	var x uintptr
   159  	for i := 0; i < b.N; i++ {
   160  		p := make([]LargeStruct, 2)
   161  		x ^= uintptr(unsafe.Pointer(&p[0]))
   162  	}
   163  	mallocSink = x
   164  }
   165  
   166  var n = flag.Int("n", 1000, "number of goroutines")
   167  
   168  func BenchmarkGoroutineSelect(b *testing.B) {
   169  	quit := make(chan struct{})
   170  	read := func(ch chan struct{}) {
   171  		for {
   172  			select {
   173  			case _, ok := <-ch:
   174  				if !ok {
   175  					return
   176  				}
   177  			case <-quit:
   178  				return
   179  			}
   180  		}
   181  	}
   182  	benchHelper(b, *n, read)
   183  }
   184  
   185  func BenchmarkGoroutineBlocking(b *testing.B) {
   186  	read := func(ch chan struct{}) {
   187  		for {
   188  			if _, ok := <-ch; !ok {
   189  				return
   190  			}
   191  		}
   192  	}
   193  	benchHelper(b, *n, read)
   194  }
   195  
   196  func BenchmarkGoroutineForRange(b *testing.B) {
   197  	read := func(ch chan struct{}) {
   198  		for range ch {
   199  		}
   200  	}
   201  	benchHelper(b, *n, read)
   202  }
   203  
   204  func benchHelper(b *testing.B, n int, read func(chan struct{})) {
   205  	m := make([]chan struct{}, n)
   206  	for i := range m {
   207  		m[i] = make(chan struct{}, 1)
   208  		go read(m[i])
   209  	}
   210  	b.StopTimer()
   211  	b.ResetTimer()
   212  	GC()
   213  
   214  	for i := 0; i < b.N; i++ {
   215  		for _, ch := range m {
   216  			if ch != nil {
   217  				ch <- struct{}{}
   218  			}
   219  		}
   220  		time.Sleep(10 * time.Millisecond)
   221  		b.StartTimer()
   222  		GC()
   223  		b.StopTimer()
   224  	}
   225  
   226  	for _, ch := range m {
   227  		close(ch)
   228  	}
   229  	time.Sleep(10 * time.Millisecond)
   230  }
   231  
   232  func BenchmarkGoroutineIdle(b *testing.B) {
   233  	quit := make(chan struct{})
   234  	fn := func() {
   235  		<-quit
   236  	}
   237  	for i := 0; i < *n; i++ {
   238  		go fn()
   239  	}
   240  
   241  	GC()
   242  	b.ResetTimer()
   243  
   244  	for i := 0; i < b.N; i++ {
   245  		GC()
   246  	}
   247  
   248  	b.StopTimer()
   249  	close(quit)
   250  	time.Sleep(10 * time.Millisecond)
   251  }