mirror of
https://github.com/golang/go
synced 2024-11-21 14:14:40 -07:00
runtime: add per-pause gc stats
R=r, r2 CC=golang-dev https://golang.org/cl/3980042
This commit is contained in:
parent
61a4e9812e
commit
bcd910cfe2
@ -39,6 +39,7 @@ type MemStatsType struct {
|
|||||||
Sys uint64 // bytes obtained from system (should be sum of XxxSys below)
|
Sys uint64 // bytes obtained from system (should be sum of XxxSys below)
|
||||||
Lookups uint64 // number of pointer lookups
|
Lookups uint64 // number of pointer lookups
|
||||||
Mallocs uint64 // number of mallocs
|
Mallocs uint64 // number of mallocs
|
||||||
|
Frees uint64 // number of frees
|
||||||
|
|
||||||
// Main allocation heap statistics.
|
// Main allocation heap statistics.
|
||||||
HeapAlloc uint64 // bytes allocated and still in use
|
HeapAlloc uint64 // bytes allocated and still in use
|
||||||
@ -60,11 +61,12 @@ type MemStatsType struct {
|
|||||||
BuckHashSys uint64 // profiling bucket hash table
|
BuckHashSys uint64 // profiling bucket hash table
|
||||||
|
|
||||||
// Garbage collector statistics.
|
// Garbage collector statistics.
|
||||||
NextGC uint64
|
NextGC uint64
|
||||||
PauseNs uint64
|
PauseTotalNs uint64
|
||||||
NumGC uint32
|
PauseNs [256]uint64 // most recent GC pause times
|
||||||
EnableGC bool
|
NumGC uint32
|
||||||
DebugGC bool
|
EnableGC bool
|
||||||
|
DebugGC bool
|
||||||
|
|
||||||
// Per-size allocation statistics.
|
// Per-size allocation statistics.
|
||||||
// Not locked during update; approximate.
|
// Not locked during update; approximate.
|
||||||
|
@ -176,6 +176,7 @@ struct MStats
|
|||||||
uint64 sys; // bytes obtained from system (should be sum of xxx_sys below)
|
uint64 sys; // bytes obtained from system (should be sum of xxx_sys below)
|
||||||
uint64 nlookup; // number of pointer lookups
|
uint64 nlookup; // number of pointer lookups
|
||||||
uint64 nmalloc; // number of mallocs
|
uint64 nmalloc; // number of mallocs
|
||||||
|
uint64 nfree; // number of frees
|
||||||
|
|
||||||
// Statistics about malloc heap.
|
// Statistics about malloc heap.
|
||||||
// protected by mheap.Lock
|
// protected by mheap.Lock
|
||||||
@ -199,7 +200,8 @@ struct MStats
|
|||||||
// Statistics about garbage collector.
|
// Statistics about garbage collector.
|
||||||
// Protected by stopping the world during GC.
|
// Protected by stopping the world during GC.
|
||||||
uint64 next_gc; // next GC (in heap_alloc time)
|
uint64 next_gc; // next GC (in heap_alloc time)
|
||||||
uint64 pause_ns;
|
uint64 pause_total_ns;
|
||||||
|
uint64 pause_ns[256];
|
||||||
uint32 numgc;
|
uint32 numgc;
|
||||||
bool enablegc;
|
bool enablegc;
|
||||||
bool debuggc;
|
bool debuggc;
|
||||||
|
@ -210,6 +210,7 @@ sweepspan(MSpan *s)
|
|||||||
case RefNone:
|
case RefNone:
|
||||||
// Free large object.
|
// Free large object.
|
||||||
mstats.alloc -= s->npages<<PageShift;
|
mstats.alloc -= s->npages<<PageShift;
|
||||||
|
mstats.nfree++;
|
||||||
runtime·memclr(p, s->npages<<PageShift);
|
runtime·memclr(p, s->npages<<PageShift);
|
||||||
if(ref & RefProfiled)
|
if(ref & RefProfiled)
|
||||||
runtime·MProf_Free(p, s->npages<<PageShift);
|
runtime·MProf_Free(p, s->npages<<PageShift);
|
||||||
@ -251,6 +252,7 @@ sweepspan(MSpan *s)
|
|||||||
if(size > sizeof(uintptr))
|
if(size > sizeof(uintptr))
|
||||||
((uintptr*)p)[1] = 1; // mark as "needs to be zeroed"
|
((uintptr*)p)[1] = 1; // mark as "needs to be zeroed"
|
||||||
mstats.alloc -= size;
|
mstats.alloc -= size;
|
||||||
|
mstats.nfree++;
|
||||||
mstats.by_size[s->sizeclass].nfree++;
|
mstats.by_size[s->sizeclass].nfree++;
|
||||||
runtime·MCache_Free(c, p, s->sizeclass, size);
|
runtime·MCache_Free(c, p, s->sizeclass, size);
|
||||||
break;
|
break;
|
||||||
@ -381,7 +383,8 @@ runtime·gc(int32 force)
|
|||||||
|
|
||||||
t1 = runtime·nanotime();
|
t1 = runtime·nanotime();
|
||||||
mstats.numgc++;
|
mstats.numgc++;
|
||||||
mstats.pause_ns += t1 - t0;
|
mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t1 - t0;
|
||||||
|
mstats.pause_total_ns += t1 - t0;
|
||||||
if(mstats.debuggc)
|
if(mstats.debuggc)
|
||||||
runtime·printf("pause %D\n", t1-t0);
|
runtime·printf("pause %D\n", t1-t0);
|
||||||
runtime·semrelease(&gcsema);
|
runtime·semrelease(&gcsema);
|
||||||
|
@ -11,8 +11,8 @@ ALL=\
|
|||||||
|
|
||||||
all: $(addsuffix .out, $(ALL))
|
all: $(addsuffix .out, $(ALL))
|
||||||
|
|
||||||
%.$O: %.go
|
%.$O: %.go stats.go
|
||||||
$(GC) $*.go
|
$(GC) $*.go stats.go
|
||||||
|
|
||||||
%.out: %.$O
|
%.out: %.$O
|
||||||
$(LD) -o $@ $*.$O
|
$(LD) -o $@ $*.$O
|
||||||
|
@ -11,13 +11,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"http"
|
||||||
|
_ "http/pprof"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var serve = flag.String("serve", "", "serve http on this address at end")
|
||||||
|
|
||||||
func isGoFile(dir *os.FileInfo) bool {
|
func isGoFile(dir *os.FileInfo) bool {
|
||||||
return dir.IsRegular() &&
|
return dir.IsRegular() &&
|
||||||
!strings.HasPrefix(dir.Name, ".") && // ignore .files
|
!strings.HasPrefix(dir.Name, ".") && // ignore .files
|
||||||
@ -30,7 +36,7 @@ func isPkgFile(dir *os.FileInfo) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pkgName(filename string) string {
|
func pkgName(filename string) string {
|
||||||
file, err := parser.ParseFile(filename, nil, parser.PackageClauseOnly)
|
file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
|
||||||
if err != nil || file == nil {
|
if err != nil || file == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -58,7 +64,7 @@ func parseDir(dirpath string) map[string]*ast.Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get package AST
|
// get package AST
|
||||||
pkgs, err := parser.ParseDir(dirpath, filter, parser.ParseComments)
|
pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("parse", dirpath, err.String())
|
println("parse", dirpath, err.String())
|
||||||
panic("fail")
|
panic("fail")
|
||||||
@ -67,12 +73,19 @@ func parseDir(dirpath string) map[string]*ast.Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
go func() {}()
|
||||||
|
go func() {}()
|
||||||
|
go func() {}()
|
||||||
st := &runtime.MemStats
|
st := &runtime.MemStats
|
||||||
|
packages = append(packages, packages...)
|
||||||
|
packages = append(packages, packages...)
|
||||||
n := flag.Int("n", 4, "iterations")
|
n := flag.Int("n", 4, "iterations")
|
||||||
p := flag.Int("p", len(packages), "# of packages to keep in memory")
|
p := flag.Int("p", len(packages), "# of packages to keep in memory")
|
||||||
flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
|
flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
var lastParsed []map[string]*ast.Package
|
||||||
var t0 int64
|
var t0 int64
|
||||||
pkgroot := runtime.GOROOT() + "/src/pkg/"
|
pkgroot := runtime.GOROOT() + "/src/pkg/"
|
||||||
for pass := 0; pass < 2; pass++ {
|
for pass := 0; pass < 2; pass++ {
|
||||||
@ -81,7 +94,7 @@ func main() {
|
|||||||
// than the normal pauses and would otherwise make
|
// than the normal pauses and would otherwise make
|
||||||
// the average look much better than it actually is.
|
// the average look much better than it actually is.
|
||||||
st.NumGC = 0
|
st.NumGC = 0
|
||||||
st.PauseNs = 0
|
st.PauseTotalNs = 0
|
||||||
t0 = time.Nanoseconds()
|
t0 = time.Nanoseconds()
|
||||||
|
|
||||||
for i := 0; i < *n; i++ {
|
for i := 0; i < *n; i++ {
|
||||||
@ -89,25 +102,34 @@ func main() {
|
|||||||
for j := range parsed {
|
for j := range parsed {
|
||||||
parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
|
parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
|
||||||
}
|
}
|
||||||
|
if i+1 == *n && *serve != "" {
|
||||||
|
lastParsed = parsed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
runtime.GC()
|
||||||
}
|
}
|
||||||
t1 := time.Nanoseconds()
|
t1 := time.Nanoseconds()
|
||||||
|
|
||||||
fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
|
fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
|
||||||
st.Alloc, st.TotalAlloc,
|
st.Alloc, st.TotalAlloc,
|
||||||
st.Sys,
|
st.Sys,
|
||||||
st.Mallocs, float64(st.PauseNs)/1e9,
|
st.Mallocs, float64(st.PauseTotalNs)/1e9,
|
||||||
st.NumGC, float64(st.PauseNs)/1e9/float64(st.NumGC))
|
st.NumGC, float64(st.PauseTotalNs)/1e9/float64(st.NumGC))
|
||||||
|
|
||||||
fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
|
|
||||||
for _, s := range st.BySize {
|
|
||||||
fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
|
||||||
|
for _, s := range st.BySize {
|
||||||
|
fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
|
||||||
|
}
|
||||||
|
*/
|
||||||
// Standard gotest benchmark output, collected by build dashboard.
|
// Standard gotest benchmark output, collected by build dashboard.
|
||||||
fmt.Printf("garbage.BenchmarkParser %d %d ns/op\n", *n, (t1-t0)/int64(*n))
|
gcstats("BenchmarkParser", *n, t1-t0)
|
||||||
fmt.Printf("garbage.BenchmarkParserPause %d %d ns/op\n", st.NumGC, int64(st.PauseNs)/int64(st.NumGC))
|
|
||||||
|
if *serve != "" {
|
||||||
|
log.Exit(http.ListenAndServe(*serve, nil))
|
||||||
|
println(lastParsed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -183,7 +205,6 @@ var packages = []string{
|
|||||||
"math",
|
"math",
|
||||||
"mime",
|
"mime",
|
||||||
"net",
|
"net",
|
||||||
"nntp",
|
|
||||||
"os",
|
"os",
|
||||||
"os/signal",
|
"os/signal",
|
||||||
"patch",
|
"patch",
|
||||||
@ -195,6 +216,7 @@ var packages = []string{
|
|||||||
"runtime",
|
"runtime",
|
||||||
"scanner",
|
"scanner",
|
||||||
"sort",
|
"sort",
|
||||||
|
"smtp",
|
||||||
"strconv",
|
"strconv",
|
||||||
"strings",
|
"strings",
|
||||||
"sync",
|
"sync",
|
||||||
|
@ -123,7 +123,6 @@ func verify() {
|
|||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := &runtime.MemStats
|
|
||||||
t0 := time.Nanoseconds()
|
t0 := time.Nanoseconds()
|
||||||
verify()
|
verify()
|
||||||
for i := 0; i <= 9; i++ {
|
for i := 0; i <= 9; i++ {
|
||||||
@ -132,6 +131,5 @@ func main() {
|
|||||||
runtime.GC()
|
runtime.GC()
|
||||||
t1 := time.Nanoseconds()
|
t1 := time.Nanoseconds()
|
||||||
|
|
||||||
fmt.Printf("garbage.BenchmarkPeano 1 %d ns/op\n", t1-t0)
|
gcstats("BenchmarkPeano", 1, t1-t0)
|
||||||
fmt.Printf("garbage.BenchmarkPeanoPause %d %d ns/op\n", st.NumGC, int64(st.PauseNs)/int64(st.NumGC))
|
|
||||||
}
|
}
|
||||||
|
44
test/garbage/stats.go
Normal file
44
test/garbage/stats.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2010 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gcstats(name string, n int, t int64) {
|
||||||
|
st := &runtime.MemStats
|
||||||
|
fmt.Printf("garbage.%sMem Alloc=%d/%d Heap=%d NextGC=%d Mallocs=%d\n", name, st.Alloc, st.TotalAlloc, st.Sys, st.NextGC, st.Mallocs)
|
||||||
|
fmt.Printf("garbage.%s %d %d ns/op\n", name, n, t/int64(n))
|
||||||
|
fmt.Printf("garbage.%sLastPause 1 %d ns/op\n", name, st.PauseNs[(st.NumGC-1)%uint32(len(st.PauseNs))])
|
||||||
|
fmt.Printf("garbage.%sPause %d %d ns/op\n", name, st.NumGC, int64(st.PauseTotalNs)/int64(st.NumGC))
|
||||||
|
nn := int(st.NumGC)
|
||||||
|
if nn >= len(st.PauseNs) {
|
||||||
|
nn = len(st.PauseNs)
|
||||||
|
}
|
||||||
|
t1, t2, t3, t4, t5 := tukey5(st.PauseNs[0:nn])
|
||||||
|
fmt.Printf("garbage.%sPause5: %d %d %d %d %d\n", name, t1, t2, t3, t4, t5)
|
||||||
|
|
||||||
|
// fmt.Printf("garbage.%sScan: %v\n", name, st.ScanDist)
|
||||||
|
}
|
||||||
|
|
||||||
|
type T []uint64
|
||||||
|
func (t T) Len() int { return len(t) }
|
||||||
|
func (t T) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||||
|
func (t T) Less(i, j int) bool { return t[i] < t[j] }
|
||||||
|
|
||||||
|
func tukey5(raw []uint64) (lo, q1, q2, q3, hi uint64) {
|
||||||
|
x := make(T, len(raw))
|
||||||
|
copy(x, raw)
|
||||||
|
sort.Sort(T(x))
|
||||||
|
lo = x[0]
|
||||||
|
q1 = x[len(x)/4]
|
||||||
|
q2 = x[len(x)/2]
|
||||||
|
q3 = x[len(x)*3/4]
|
||||||
|
hi = x[len(x)-1]
|
||||||
|
return
|
||||||
|
}
|
@ -39,7 +39,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,10 +94,7 @@ func main() {
|
|||||||
fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, longLivedTree.itemCheck())
|
fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, longLivedTree.itemCheck())
|
||||||
|
|
||||||
t1 := time.Nanoseconds()
|
t1 := time.Nanoseconds()
|
||||||
st := &runtime.MemStats
|
|
||||||
|
|
||||||
// Standard gotest benchmark output, collected by build dashboard.
|
// Standard gotest benchmark output, collected by build dashboard.
|
||||||
fmt.Printf("garbage.BenchmarkTree %d %d ns/op\n", *n, (t1-t0)/int64(*n))
|
gcstats("BenchmarkTree", *n, t1-t0)
|
||||||
fmt.Printf("garbage.BenchmarkTreePause %d %d ns/op\n", st.NumGC, int64(st.PauseNs)/int64(st.NumGC))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user