mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
When a new tiny block is allocated because we're allocating an object that won't fit into the current block, mallocgc saves the new block if it has more space leftover than the old block. However, the logic for this was subtly broken in golang.org/cl/2814, resulting in never saving (or consequently reusing) a tiny block. Change-Id: Ib5f6769451fb82877ddeefe75dfe79ed4a04fd40 Reviewed-on: https://go-review.googlesource.com/16330 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
242 lines
5.3 KiB
Go
242 lines
5.3 KiB
Go
// 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)
|
|
}
|