github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/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  	"fmt"
    10  	"reflect"
    11  	. "runtime"
    12  	"testing"
    13  	"time"
    14  	"unsafe"
    15  )
    16  
    17  func TestMemStats(t *testing.T) {
    18  	// Make sure there's at least one forced GC.
    19  	GC()
    20  
    21  	// Test that MemStats has sane values.
    22  	st := new(MemStats)
    23  	ReadMemStats(st)
    24  
    25  	nz := func(x interface{}) error {
    26  		if x != reflect.Zero(reflect.TypeOf(x)).Interface() {
    27  			return nil
    28  		}
    29  		return fmt.Errorf("zero value")
    30  	}
    31  	le := func(thresh float64) func(interface{}) error {
    32  		return func(x interface{}) error {
    33  			if reflect.ValueOf(x).Convert(reflect.TypeOf(thresh)).Float() < thresh {
    34  				return nil
    35  			}
    36  			return fmt.Errorf("insanely high value (overflow?); want <= %v", thresh)
    37  		}
    38  	}
    39  	eq := func(x interface{}) func(interface{}) error {
    40  		return func(y interface{}) error {
    41  			if x == y {
    42  				return nil
    43  			}
    44  			return fmt.Errorf("want %v", x)
    45  		}
    46  	}
    47  	// Of the uint fields, HeapReleased, HeapIdle can be 0.
    48  	// PauseTotalNs can be 0 if timer resolution is poor.
    49  	//
    50  	// TODO: Test that GCCPUFraction is <= 0.99. This currently
    51  	// fails on windows/386. (Issue #19319)
    52  	fields := map[string][]func(interface{}) error{
    53  		"Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)},
    54  		"Lookups": {nz, le(1e10)}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)},
    55  		"HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)},
    56  		"HeapInuse": {nz, le(1e10)}, "HeapReleased": {le(1e10)}, "HeapObjects": {nz, le(1e10)},
    57  		"StackInuse": {nz, le(1e10)}, "StackSys": {nz, le(1e10)},
    58  		"MSpanInuse": {nz, le(1e10)}, "MSpanSys": {nz, le(1e10)},
    59  		"MCacheInuse": {nz, le(1e10)}, "MCacheSys": {nz, le(1e10)},
    60  		"BuckHashSys": {nz, le(1e10)}, "GCSys": {nz, le(1e10)}, "OtherSys": {nz, le(1e10)},
    61  		"NextGC": {nz, le(1e10)}, "LastGC": {nz},
    62  		"PauseTotalNs": {le(1e11)}, "PauseNs": nil, "PauseEnd": nil,
    63  		"NumGC": {nz, le(1e9)}, "NumForcedGC": {nz, le(1e9)},
    64  		"GCCPUFraction": nil, "EnableGC": {eq(true)}, "DebugGC": {eq(false)},
    65  		"BySize": nil,
    66  	}
    67  
    68  	rst := reflect.ValueOf(st).Elem()
    69  	for i := 0; i < rst.Type().NumField(); i++ {
    70  		name, val := rst.Type().Field(i).Name, rst.Field(i).Interface()
    71  		checks, ok := fields[name]
    72  		if !ok {
    73  			t.Errorf("unknown MemStats field %s", name)
    74  			continue
    75  		}
    76  		for _, check := range checks {
    77  			if err := check(val); err != nil {
    78  				t.Errorf("%s = %v: %s", name, val, err)
    79  			}
    80  		}
    81  	}
    82  
    83  	if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
    84  		st.BuckHashSys+st.GCSys+st.OtherSys {
    85  		t.Fatalf("Bad sys value: %+v", *st)
    86  	}
    87  
    88  	if st.HeapIdle+st.HeapInuse != st.HeapSys {
    89  		t.Fatalf("HeapIdle(%d) + HeapInuse(%d) should be equal to HeapSys(%d), but isn't.", st.HeapIdle, st.HeapInuse, st.HeapSys)
    90  	}
    91  
    92  	if lpe := st.PauseEnd[int(st.NumGC+255)%len(st.PauseEnd)]; st.LastGC != lpe {
    93  		t.Fatalf("LastGC(%d) != last PauseEnd(%d)", st.LastGC, lpe)
    94  	}
    95  
    96  	var pauseTotal uint64
    97  	for _, pause := range st.PauseNs {
    98  		pauseTotal += pause
    99  	}
   100  	if int(st.NumGC) < len(st.PauseNs) {
   101  		// We have all pauses, so this should be exact.
   102  		if st.PauseTotalNs != pauseTotal {
   103  			t.Fatalf("PauseTotalNs(%d) != sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
   104  		}
   105  		for i := int(st.NumGC); i < len(st.PauseNs); i++ {
   106  			if st.PauseNs[i] != 0 {
   107  				t.Fatalf("Non-zero PauseNs[%d]: %+v", i, st)
   108  			}
   109  			if st.PauseEnd[i] != 0 {
   110  				t.Fatalf("Non-zero PauseEnd[%d]: %+v", i, st)
   111  			}
   112  		}
   113  	} else {
   114  		if st.PauseTotalNs < pauseTotal {
   115  			t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
   116  		}
   117  	}
   118  
   119  	if st.NumForcedGC > st.NumGC {
   120  		t.Fatalf("NumForcedGC(%d) > NumGC(%d)", st.NumForcedGC, st.NumGC)
   121  	}
   122  }
   123  
   124  func TestStringConcatenationAllocs(t *testing.T) {
   125  	n := testing.AllocsPerRun(1e3, func() {
   126  		b := make([]byte, 10)
   127  		for i := 0; i < 10; i++ {
   128  			b[i] = byte(i) + '0'
   129  		}
   130  		s := "foo" + string(b)
   131  		if want := "foo0123456789"; s != want {
   132  			t.Fatalf("want %v, got %v", want, s)
   133  		}
   134  	})
   135  	// Only string concatenation allocates.
   136  	if n != 1 {
   137  		t.Fatalf("want 1 allocation, got %v", n)
   138  	}
   139  }
   140  
   141  func TestTinyAlloc(t *testing.T) {
   142  	const N = 16
   143  	var v [N]unsafe.Pointer
   144  	for i := range v {
   145  		v[i] = unsafe.Pointer(new(byte))
   146  	}
   147  
   148  	chunks := make(map[uintptr]bool, N)
   149  	for _, p := range v {
   150  		chunks[uintptr(p)&^7] = true
   151  	}
   152  
   153  	if len(chunks) == N {
   154  		t.Fatal("no bytes allocated within the same 8-byte chunk")
   155  	}
   156  }
   157  
   158  var mallocSink uintptr
   159  
   160  func BenchmarkMalloc8(b *testing.B) {
   161  	var x uintptr
   162  	for i := 0; i < b.N; i++ {
   163  		p := new(int64)
   164  		x ^= uintptr(unsafe.Pointer(p))
   165  	}
   166  	mallocSink = x
   167  }
   168  
   169  func BenchmarkMalloc16(b *testing.B) {
   170  	var x uintptr
   171  	for i := 0; i < b.N; i++ {
   172  		p := new([2]int64)
   173  		x ^= uintptr(unsafe.Pointer(p))
   174  	}
   175  	mallocSink = x
   176  }
   177  
   178  func BenchmarkMallocTypeInfo8(b *testing.B) {
   179  	var x uintptr
   180  	for i := 0; i < b.N; i++ {
   181  		p := new(struct {
   182  			p [8 / unsafe.Sizeof(uintptr(0))]*int
   183  		})
   184  		x ^= uintptr(unsafe.Pointer(p))
   185  	}
   186  	mallocSink = x
   187  }
   188  
   189  func BenchmarkMallocTypeInfo16(b *testing.B) {
   190  	var x uintptr
   191  	for i := 0; i < b.N; i++ {
   192  		p := new(struct {
   193  			p [16 / unsafe.Sizeof(uintptr(0))]*int
   194  		})
   195  		x ^= uintptr(unsafe.Pointer(p))
   196  	}
   197  	mallocSink = x
   198  }
   199  
   200  type LargeStruct struct {
   201  	x [16][]byte
   202  }
   203  
   204  func BenchmarkMallocLargeStruct(b *testing.B) {
   205  	var x uintptr
   206  	for i := 0; i < b.N; i++ {
   207  		p := make([]LargeStruct, 2)
   208  		x ^= uintptr(unsafe.Pointer(&p[0]))
   209  	}
   210  	mallocSink = x
   211  }
   212  
   213  var n = flag.Int("n", 1000, "number of goroutines")
   214  
   215  func BenchmarkGoroutineSelect(b *testing.B) {
   216  	quit := make(chan struct{})
   217  	read := func(ch chan struct{}) {
   218  		for {
   219  			select {
   220  			case _, ok := <-ch:
   221  				if !ok {
   222  					return
   223  				}
   224  			case <-quit:
   225  				return
   226  			}
   227  		}
   228  	}
   229  	benchHelper(b, *n, read)
   230  }
   231  
   232  func BenchmarkGoroutineBlocking(b *testing.B) {
   233  	read := func(ch chan struct{}) {
   234  		for {
   235  			if _, ok := <-ch; !ok {
   236  				return
   237  			}
   238  		}
   239  	}
   240  	benchHelper(b, *n, read)
   241  }
   242  
   243  func BenchmarkGoroutineForRange(b *testing.B) {
   244  	read := func(ch chan struct{}) {
   245  		for range ch {
   246  		}
   247  	}
   248  	benchHelper(b, *n, read)
   249  }
   250  
   251  func benchHelper(b *testing.B, n int, read func(chan struct{})) {
   252  	m := make([]chan struct{}, n)
   253  	for i := range m {
   254  		m[i] = make(chan struct{}, 1)
   255  		go read(m[i])
   256  	}
   257  	b.StopTimer()
   258  	b.ResetTimer()
   259  	GC()
   260  
   261  	for i := 0; i < b.N; i++ {
   262  		for _, ch := range m {
   263  			if ch != nil {
   264  				ch <- struct{}{}
   265  			}
   266  		}
   267  		time.Sleep(10 * time.Millisecond)
   268  		b.StartTimer()
   269  		GC()
   270  		b.StopTimer()
   271  	}
   272  
   273  	for _, ch := range m {
   274  		close(ch)
   275  	}
   276  	time.Sleep(10 * time.Millisecond)
   277  }
   278  
   279  func BenchmarkGoroutineIdle(b *testing.B) {
   280  	quit := make(chan struct{})
   281  	fn := func() {
   282  		<-quit
   283  	}
   284  	for i := 0; i < *n; i++ {
   285  		go fn()
   286  	}
   287  
   288  	GC()
   289  	b.ResetTimer()
   290  
   291  	for i := 0; i < b.N; i++ {
   292  		GC()
   293  	}
   294  
   295  	b.StopTimer()
   296  	close(quit)
   297  	time.Sleep(10 * time.Millisecond)
   298  }