github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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 }