2013-05-15 21:22:32 +04:00
|
|
|
// 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 (
|
2013-10-02 11:59:53 -04:00
|
|
|
"flag"
|
2017-02-23 21:40:55 -05:00
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
. "runtime"
|
2013-05-15 21:22:32 +04:00
|
|
|
"testing"
|
2013-10-02 11:59:53 -04:00
|
|
|
"time"
|
2013-05-15 21:22:32 +04:00
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
func TestMemStats(t *testing.T) {
|
2016-12-06 17:42:42 -05:00
|
|
|
// Make sure there's at least one forced GC.
|
|
|
|
|
GC()
|
|
|
|
|
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
// Test that MemStats has sane values.
|
|
|
|
|
st := new(MemStats)
|
|
|
|
|
ReadMemStats(st)
|
2014-08-19 11:46:05 +04:00
|
|
|
|
2017-02-23 21:40:55 -05:00
|
|
|
nz := func(x interface{}) error {
|
|
|
|
|
if x != reflect.Zero(reflect.TypeOf(x)).Interface() {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("zero value")
|
|
|
|
|
}
|
2017-02-23 21:48:34 -05:00
|
|
|
le := func(thresh float64) func(interface{}) error {
|
2017-02-23 21:40:55 -05:00
|
|
|
return func(x interface{}) error {
|
2017-02-23 21:48:34 -05:00
|
|
|
if reflect.ValueOf(x).Convert(reflect.TypeOf(thresh)).Float() < thresh {
|
2017-02-23 21:40:55 -05:00
|
|
|
return nil
|
|
|
|
|
}
|
2017-02-23 21:48:34 -05:00
|
|
|
return fmt.Errorf("insanely high value (overflow?); want <= %v", thresh)
|
2017-02-23 21:40:55 -05:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-23 21:48:34 -05:00
|
|
|
eq := func(x interface{}) func(interface{}) error {
|
|
|
|
|
return func(y interface{}) error {
|
|
|
|
|
if x == y {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("want %v", x)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Of the uint fields, HeapReleased, HeapIdle can be 0.
|
|
|
|
|
// PauseTotalNs can be 0 if timer resolution is poor.
|
2017-02-23 21:40:55 -05:00
|
|
|
fields := map[string][]func(interface{}) error{
|
|
|
|
|
"Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)},
|
|
|
|
|
"Lookups": {nz, le(1e10)}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)},
|
|
|
|
|
"HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)},
|
2017-02-23 21:48:34 -05:00
|
|
|
"HeapInuse": {nz, le(1e10)}, "HeapReleased": {le(1e10)}, "HeapObjects": {nz, le(1e10)},
|
2017-02-23 21:40:55 -05:00
|
|
|
"StackInuse": {nz, le(1e10)}, "StackSys": {nz, le(1e10)},
|
|
|
|
|
"MSpanInuse": {nz, le(1e10)}, "MSpanSys": {nz, le(1e10)},
|
|
|
|
|
"MCacheInuse": {nz, le(1e10)}, "MCacheSys": {nz, le(1e10)},
|
|
|
|
|
"BuckHashSys": {nz, le(1e10)}, "GCSys": {nz, le(1e10)}, "OtherSys": {nz, le(1e10)},
|
2017-02-23 21:48:34 -05:00
|
|
|
"NextGC": {nz, le(1e10)}, "LastGC": {nz},
|
2017-02-23 21:40:55 -05:00
|
|
|
"PauseTotalNs": {le(1e11)}, "PauseNs": nil, "PauseEnd": nil,
|
2017-02-23 21:48:34 -05:00
|
|
|
"NumGC": {nz, le(1e9)}, "NumForcedGC": {nz, le(1e9)},
|
2017-07-10 14:34:26 -04:00
|
|
|
"GCCPUFraction": {le(0.99)}, "EnableGC": {eq(true)}, "DebugGC": {eq(false)},
|
2017-02-23 21:40:55 -05:00
|
|
|
"BySize": nil,
|
2014-08-19 11:46:05 +04:00
|
|
|
}
|
|
|
|
|
|
2017-02-23 21:40:55 -05:00
|
|
|
rst := reflect.ValueOf(st).Elem()
|
|
|
|
|
for i := 0; i < rst.Type().NumField(); i++ {
|
|
|
|
|
name, val := rst.Type().Field(i).Name, rst.Field(i).Interface()
|
|
|
|
|
checks, ok := fields[name]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("unknown MemStats field %s", name)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, check := range checks {
|
|
|
|
|
if err := check(val); err != nil {
|
|
|
|
|
t.Errorf("%s = %v: %s", name, val, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
}
|
2014-08-19 11:46:05 +04:00
|
|
|
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+
|
|
|
|
|
st.BuckHashSys+st.GCSys+st.OtherSys {
|
|
|
|
|
t.Fatalf("Bad sys value: %+v", *st)
|
|
|
|
|
}
|
2014-08-25 11:29:53 +04:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2015-06-30 18:20:13 -04:00
|
|
|
|
|
|
|
|
if lpe := st.PauseEnd[int(st.NumGC+255)%len(st.PauseEnd)]; st.LastGC != lpe {
|
|
|
|
|
t.Fatalf("LastGC(%d) != last PauseEnd(%d)", st.LastGC, lpe)
|
|
|
|
|
}
|
2015-07-01 11:04:19 -04:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2015-12-14 18:04:40 -05:00
|
|
|
for i := int(st.NumGC); i < len(st.PauseNs); i++ {
|
|
|
|
|
if st.PauseNs[i] != 0 {
|
|
|
|
|
t.Fatalf("Non-zero PauseNs[%d]: %+v", i, st)
|
|
|
|
|
}
|
|
|
|
|
if st.PauseEnd[i] != 0 {
|
|
|
|
|
t.Fatalf("Non-zero PauseEnd[%d]: %+v", i, st)
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-01 11:04:19 -04:00
|
|
|
} else {
|
|
|
|
|
if st.PauseTotalNs < pauseTotal {
|
|
|
|
|
t.Fatalf("PauseTotalNs(%d) < sum PauseNs(%d)", st.PauseTotalNs, pauseTotal)
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-06 17:42:42 -05:00
|
|
|
|
|
|
|
|
if st.NumForcedGC > st.NumGC {
|
|
|
|
|
t.Fatalf("NumForcedGC(%d) > NumGC(%d)", st.NumForcedGC, st.NumGC)
|
|
|
|
|
}
|
runtime: account for all sys memory in MemStats
Currently lots of sys allocations are not accounted in any of XxxSys,
including GC bitmap, spans table, GC roots blocks, GC finalizer blocks,
iface table, netpoll descriptors and more. Up to ~20% can unaccounted.
This change introduces 2 new stats: GCSys and OtherSys for GC metadata
and all other misc allocations, respectively.
Also ensures that all XxxSys indeed sum up to Sys. All sys memory allocation
functions require the stat for accounting, so that it's impossible to miss something.
Also fix updating of mcache_sys/inuse, they were not updated after deallocation.
test/bench/garbage/parser before:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14204928
MCacheSys 16384
BuckHashSys 1439992
after:
Sys 670064344
HeapSys 610271232
StackSys 65536
MSpanSys 14188544
MCacheSys 16384
BuckHashSys 3194304
GCSys 39198688
OtherSys 3129656
Fixes #5799.
R=rsc, dave, alex.brainman
CC=golang-dev
https://golang.org/cl/12946043
2013-09-06 16:55:40 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-22 17:56:12 +03:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 12:38:47 -07:00
|
|
|
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")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-15 21:22:32 +04:00
|
|
|
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
|
|
|
|
|
}
|
2013-10-02 11:59:53 -04:00
|
|
|
|
2014-07-29 11:01:02 +04:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 11:59:53 -04:00
|
|
|
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{}) {
|
2014-07-16 16:29:51 -07:00
|
|
|
for range ch {
|
2013-10-02 11:59:53 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|