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