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