mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: make ReadMemStats STW for < 25µs
Currently ReadMemStats stops the world for ~1.7 ms/GB of heap because it collects statistics from every single span. For large heaps, this can be quite costly. This is particularly unfortunate because many production infrastructures call this function regularly to collect and report statistics. Fix this by tracking the necessary cumulative statistics in the mcaches. ReadMemStats still has to stop the world to stabilize these statistics, but there are only O(GOMAXPROCS) mcaches to collect statistics from, so this pause is only 25µs even at GOMAXPROCS=100. Fixes #13613. Change-Id: I3c0a4e14833f4760dab675efc1916e73b4c0032a Reviewed-on: https://go-review.googlesource.com/34937 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rick Hudson <rlh@golang.org>
This commit is contained in:
parent
3399fd254d
commit
4a7cf960c3
5 changed files with 158 additions and 26 deletions
|
|
@ -5,6 +5,7 @@
|
|||
package runtime_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
|
@ -448,3 +449,53 @@ func TestPageAccounting(t *testing.T) {
|
|||
t.Fatalf("mheap_.pagesInUse is %d, but direct count is %d", pagesInUse, counted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadMemStats(t *testing.T) {
|
||||
base, slow := runtime.ReadMemStatsSlow()
|
||||
if base != slow {
|
||||
logDiff(t, "MemStats", reflect.ValueOf(base), reflect.ValueOf(slow))
|
||||
t.Fatal("memstats mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func logDiff(t *testing.T, prefix string, got, want reflect.Value) {
|
||||
typ := got.Type()
|
||||
switch typ.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if got.Len() != want.Len() {
|
||||
t.Logf("len(%s): got %v, want %v", prefix, got, want)
|
||||
return
|
||||
}
|
||||
for i := 0; i < got.Len(); i++ {
|
||||
logDiff(t, fmt.Sprintf("%s[%d]", prefix, i), got.Index(i), want.Index(i))
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
gf, wf := got.Field(i), want.Field(i)
|
||||
logDiff(t, prefix+"."+typ.Field(i).Name, gf, wf)
|
||||
}
|
||||
case reflect.Map:
|
||||
t.Fatal("not implemented: logDiff for map")
|
||||
default:
|
||||
if got.Interface() != want.Interface() {
|
||||
t.Logf("%s: got %v, want %v", prefix, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReadMemStats(b *testing.B) {
|
||||
var ms runtime.MemStats
|
||||
const heapSize = 100 << 20
|
||||
x := make([]*[1024]byte, heapSize/1024)
|
||||
for i := range x {
|
||||
x[i] = new([1024]byte)
|
||||
}
|
||||
hugeSink = x
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
runtime.ReadMemStats(&ms)
|
||||
}
|
||||
|
||||
hugeSink = nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue