Matthew Dempskyruntime: fix tiny allocatord18167f11 years agoHistory
242 lines · 5.3 KB · Go
RawDownload
// Copyright 2013 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file. package runtime_test import (	"flag"	. "runtime"	"testing"	"time"	"unsafe") func TestMemStats(t *testing.T) {	// Test that MemStats has sane values.	st := new(MemStats)	ReadMemStats(st) 	// Everything except HeapReleased and HeapIdle, because they indeed can be 0.	if st.Alloc == 0 || st.TotalAlloc == 0 || st.Sys == 0 || st.Lookups == 0 ||		st.Mallocs == 0 || st.Frees == 0 || st.HeapAlloc == 0 || st.HeapSys == 0 ||		st.HeapInuse == 0 || st.HeapObjects == 0 || st.StackInuse == 0 ||		st.StackSys == 0 || st.MSpanInuse == 0 || st.MSpanSys == 0 || st.MCacheInuse == 0 ||		st.MCacheSys == 0 || st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 ||		st.NextGC == 0 || st.NumGC == 0 {		t.Fatalf("Zero value: %+v", *st)	} 	if st.Alloc > 1e10 || st.TotalAlloc > 1e11 || st.Sys > 1e10 || st.Lookups > 1e10 ||		st.Mallocs > 1e10 || st.Frees > 1e10 || st.HeapAlloc > 1e10 || st.HeapSys > 1e10 ||		st.HeapIdle > 1e10 || st.HeapInuse > 1e10 || st.HeapObjects > 1e10 || st.StackInuse > 1e10 ||		st.StackSys > 1e10 || st.MSpanInuse > 1e10 || st.MSpanSys > 1e10 || st.MCacheInuse > 1e10 ||		st.MCacheSys > 1e10 || st.BuckHashSys > 1e10 || st.GCSys > 1e10 || st.OtherSys > 1e10 ||		st.NextGC > 1e10 || st.NumGC > 1e9 || st.PauseTotalNs > 1e11 {		t.Fatalf("Insanely high value (overflow?): %+v", *st)	} 	if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+		st.BuckHashSys+st.GCSys+st.OtherSys {		t.Fatalf("Bad sys value: %+v", *st)	} 	if st.HeapIdle+st.HeapInuse != st.HeapSys {		t.Fatalf("HeapIdle(%d) + HeapInuse(%d) should be equal to HeapSys(%d), but isn't.", st.HeapIdle, st.HeapInuse, st.HeapSys)	} 	if lpe := st.PauseEnd[int(st.NumGC+255)%len(st.PauseEnd)]; st.LastGC != lpe {		t.Fatalf("LastGC(%d) != last PauseEnd(%d)", st.LastGC, lpe)	} 	var pauseTotal uint64	for _, pause := range st.PauseNs {		pauseTotal += pause	}	if int(st.NumGC) < len(st.PauseNs) {		// We have all pauses, so this should be exact.		if st.PauseTotalNs != pauseTotal {			t.Fatalf("PauseTotalNs(%d) != sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)		}	} else {		if st.PauseTotalNs < pauseTotal {			t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)		}	}} func TestStringConcatenationAllocs(t *testing.T) {	n := testing.AllocsPerRun(1e3, func() {		b := make([]byte, 10)		for i := 0; i < 10; i++ {			b[i] = byte(i) + '0'		}		s := "foo" + string(b)		if want := "foo0123456789"; s != want {			t.Fatalf("want %v, got %v", want, s)		}	})	// Only string concatenation allocates.	if n != 1 {		t.Fatalf("want 1 allocation, got %v", n)	}} func TestTinyAlloc(t *testing.T) {	const N = 16	var v [N]unsafe.Pointer	for i := range v {		v[i] = unsafe.Pointer(new(byte))	} 	chunks := make(map[uintptr]bool, N)	for _, p := range v {		chunks[uintptr(p)&^7] = true	} 	if len(chunks) == N {		t.Fatal("no bytes allocated within the same 8-byte chunk")	}} var mallocSink uintptr func BenchmarkMalloc8(b *testing.B) {	var x uintptr	for i := 0; i < b.N; i++ {		p := new(int64)		x ^= uintptr(unsafe.Pointer(p))	}	mallocSink = x} func BenchmarkMalloc16(b *testing.B) {	var x uintptr	for i := 0; i < b.N; i++ {		p := new([2]int64)		x ^= uintptr(unsafe.Pointer(p))	}	mallocSink = x} func BenchmarkMallocTypeInfo8(b *testing.B) {	var x uintptr	for i := 0; i < b.N; i++ {		p := new(struct {			p [8 / unsafe.Sizeof(uintptr(0))]*int		})		x ^= uintptr(unsafe.Pointer(p))	}	mallocSink = x} func BenchmarkMallocTypeInfo16(b *testing.B) {	var x uintptr	for i := 0; i < b.N; i++ {		p := new(struct {			p [16 / unsafe.Sizeof(uintptr(0))]*int		})		x ^= uintptr(unsafe.Pointer(p))	}	mallocSink = x} type LargeStruct struct {	x [16][]byte} func BenchmarkMallocLargeStruct(b *testing.B) {	var x uintptr	for i := 0; i < b.N; i++ {		p := make([]LargeStruct, 2)		x ^= uintptr(unsafe.Pointer(&p[0]))	}	mallocSink = x} var n = flag.Int("n", 1000, "number of goroutines") func BenchmarkGoroutineSelect(b *testing.B) {	quit := make(chan struct{})	read := func(ch chan struct{}) {		for {			select {			case _, ok := <-ch:				if !ok {					return				}			case <-quit:				return			}		}	}	benchHelper(b, *n, read)} func BenchmarkGoroutineBlocking(b *testing.B) {	read := func(ch chan struct{}) {		for {			if _, ok := <-ch; !ok {				return			}		}	}	benchHelper(b, *n, read)} func BenchmarkGoroutineForRange(b *testing.B) {	read := func(ch chan struct{}) {		for range ch {		}	}	benchHelper(b, *n, read)} func benchHelper(b *testing.B, n int, read func(chan struct{})) {	m := make([]chan struct{}, n)	for i := range m {		m[i] = make(chan struct{}, 1)		go read(m[i])	}	b.StopTimer()	b.ResetTimer()	GC() 	for i := 0; i < b.N; i++ {		for _, ch := range m {			if ch != nil {				ch <- struct{}{}			}		}		time.Sleep(10 * time.Millisecond)		b.StartTimer()		GC()		b.StopTimer()	} 	for _, ch := range m {		close(ch)	}	time.Sleep(10 * time.Millisecond)} func BenchmarkGoroutineIdle(b *testing.B) {	quit := make(chan struct{})	fn := func() {		<-quit	}	for i := 0; i < *n; i++ {		go fn()	} 	GC()	b.ResetTimer() 	for i := 0; i < b.N; i++ {		GC()	} 	b.StopTimer()	close(quit)	time.Sleep(10 * time.Millisecond)}