...
Run Format

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

View as plain text