mirror of
https://github.com/golang/go
synced 2024-11-21 21:14:47 -07:00
runtime: make garbage collector faster by deleting code
Suggested by Sanjay Ghemawat. 5-20% faster depending on the benchmark. Add tree2 garbage benchmark. Update other garbage benchmarks to build again. R=golang-dev, r, adg CC=golang-dev https://golang.org/cl/5530074
This commit is contained in:
parent
2b6288113e
commit
a6d8b483b6
@ -123,10 +123,9 @@ enum
|
||||
|
||||
// Max number of threads to run garbage collection.
|
||||
// 2, 3, and 4 are all plausible maximums depending
|
||||
// on the hardware details of the machine. The second
|
||||
// proc is the one that helps the most (after the first),
|
||||
// so start with just 2 for now.
|
||||
MaxGcproc = 2,
|
||||
// on the hardware details of the machine. The garbage
|
||||
// collector scales well to 4 cpus.
|
||||
MaxGcproc = 4,
|
||||
};
|
||||
|
||||
// A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).)
|
||||
|
@ -53,9 +53,6 @@ enum {
|
||||
#define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial)
|
||||
|
||||
// TODO: Make these per-M.
|
||||
static uint64 nlookup;
|
||||
static uint64 nsizelookup;
|
||||
static uint64 naddrlookup;
|
||||
static uint64 nhandoff;
|
||||
|
||||
static int32 gctrace;
|
||||
@ -212,8 +209,6 @@ scanblock(byte *b, int64 n)
|
||||
|
||||
// Otherwise consult span table to find beginning.
|
||||
// (Manually inlined copy of MHeap_LookupMaybe.)
|
||||
nlookup++;
|
||||
naddrlookup++;
|
||||
k = (uintptr)obj>>PageShift;
|
||||
x = k;
|
||||
if(sizeof(void*) == 8)
|
||||
@ -301,49 +296,8 @@ scanblock(byte *b, int64 n)
|
||||
b = *--wp;
|
||||
nobj--;
|
||||
|
||||
// Figure out n = size of b. Start by loading bits for b.
|
||||
off = (uintptr*)b - (uintptr*)arena_start;
|
||||
bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
|
||||
shift = off % wordsPerBitmapWord;
|
||||
xbits = *bitp;
|
||||
bits = xbits >> shift;
|
||||
|
||||
// Might be small; look for nearby block boundary.
|
||||
// A block boundary is marked by either bitBlockBoundary
|
||||
// or bitAllocated being set (see notes near their definition).
|
||||
enum {
|
||||
boundary = bitBlockBoundary|bitAllocated
|
||||
};
|
||||
// Look for a block boundary both after and before b
|
||||
// in the same bitmap word.
|
||||
//
|
||||
// A block boundary j words after b is indicated by
|
||||
// bits>>j & boundary
|
||||
// assuming shift+j < bitShift. (If shift+j >= bitShift then
|
||||
// we'll be bleeding other bit types like bitMarked into our test.)
|
||||
// Instead of inserting the conditional shift+j < bitShift into the loop,
|
||||
// we can let j range from 1 to bitShift as long as we first
|
||||
// apply a mask to keep only the bits corresponding
|
||||
// to shift+j < bitShift aka j < bitShift-shift.
|
||||
bits &= (boundary<<(bitShift-shift)) - boundary;
|
||||
|
||||
// A block boundary j words before b is indicated by
|
||||
// xbits>>(shift-j) & boundary
|
||||
// (assuming shift >= j). There is no cleverness here
|
||||
// avoid the test, because when j gets too large the shift
|
||||
// turns negative, which is undefined in C.
|
||||
|
||||
for(j=1; j<bitShift; j++) {
|
||||
if(((bits>>j)&boundary) != 0 || shift>=j && ((xbits>>(shift-j))&boundary) != 0) {
|
||||
n = j*PtrSize;
|
||||
goto scan;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to asking span about size class.
|
||||
// Ask span about size class.
|
||||
// (Manually inlined copy of MHeap_Lookup.)
|
||||
nlookup++;
|
||||
nsizelookup++;
|
||||
x = (uintptr)b>>PageShift;
|
||||
if(sizeof(void*) == 8)
|
||||
x -= (uintptr)arena_start>>PageShift;
|
||||
@ -352,7 +306,6 @@ scanblock(byte *b, int64 n)
|
||||
n = s->npages<<PageShift;
|
||||
else
|
||||
n = runtime·class_to_size[s->sizeclass];
|
||||
scan:;
|
||||
}
|
||||
}
|
||||
|
||||
@ -953,9 +906,6 @@ runtime·gc(int32 force)
|
||||
}
|
||||
|
||||
t0 = runtime·nanotime();
|
||||
nlookup = 0;
|
||||
nsizelookup = 0;
|
||||
naddrlookup = 0;
|
||||
nhandoff = 0;
|
||||
|
||||
m->gcing = 1;
|
||||
@ -1020,11 +970,11 @@ runtime·gc(int32 force)
|
||||
runtime·printf("pause %D\n", t3-t0);
|
||||
|
||||
if(gctrace) {
|
||||
runtime·printf("gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D pointer lookups (%D size, %D addr) %D handoff\n",
|
||||
runtime·printf("gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D handoff\n",
|
||||
mstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000,
|
||||
heap0>>20, heap1>>20, obj0, obj1,
|
||||
mstats.nmalloc, mstats.nfree,
|
||||
nlookup, nsizelookup, naddrlookup, nhandoff);
|
||||
nhandoff);
|
||||
}
|
||||
|
||||
runtime·semrelease(&gcsema);
|
||||
|
@ -8,6 +8,7 @@ ALL=\
|
||||
parser\
|
||||
peano\
|
||||
tree\
|
||||
tree2\
|
||||
|
||||
all: $(addsuffix .out, $(ALL))
|
||||
|
||||
|
@ -24,15 +24,15 @@ import (
|
||||
|
||||
var serve = flag.String("serve", "", "serve http on this address at end")
|
||||
|
||||
func isGoFile(dir *os.FileInfo) bool {
|
||||
return dir.IsRegular() &&
|
||||
!strings.HasPrefix(dir.Name, ".") && // ignore .files
|
||||
path.Ext(dir.Name) == ".go"
|
||||
func isGoFile(dir os.FileInfo) bool {
|
||||
return !dir.IsDir() &&
|
||||
!strings.HasPrefix(dir.Name(), ".") && // ignore .files
|
||||
path.Ext(dir.Name()) == ".go"
|
||||
}
|
||||
|
||||
func isPkgFile(dir *os.FileInfo) bool {
|
||||
func isPkgFile(dir os.FileInfo) bool {
|
||||
return isGoFile(dir) &&
|
||||
!strings.HasSuffix(dir.Name, "_test.go") // ignore test files
|
||||
!strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
|
||||
}
|
||||
|
||||
func pkgName(filename string) string {
|
||||
@ -49,7 +49,7 @@ func parseDir(dirpath string) map[string]*ast.Package {
|
||||
_, pkgname := path.Split(dirpath)
|
||||
|
||||
// filter function to select the desired .go files
|
||||
filter := func(d *os.FileInfo) bool {
|
||||
filter := func(d os.FileInfo) bool {
|
||||
if isPkgFile(d) {
|
||||
// Some directories contain main packages: Only accept
|
||||
// files that belong to the expected package so that
|
||||
@ -57,7 +57,7 @@ func parseDir(dirpath string) map[string]*ast.Package {
|
||||
// found" errors.
|
||||
// Additionally, accept the special package name
|
||||
// fakePkgName if we are looking at cmd documentation.
|
||||
name := pkgName(dirpath + "/" + d.Name)
|
||||
name := pkgName(dirpath + "/" + d.Name())
|
||||
return name == pkgname
|
||||
}
|
||||
return false
|
||||
@ -82,7 +82,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var lastParsed []map[string]*ast.Package
|
||||
var t0 int64
|
||||
var t0 time.Time
|
||||
pkgroot := runtime.GOROOT() + "/src/pkg/"
|
||||
for pass := 0; pass < 2; pass++ {
|
||||
// Once the heap is grown to full size, reset counters.
|
||||
@ -91,7 +91,7 @@ func main() {
|
||||
// the average look much better than it actually is.
|
||||
st.NumGC = 0
|
||||
st.PauseTotalNs = 0
|
||||
t0 = time.Nanoseconds()
|
||||
t0 = time.Now()
|
||||
|
||||
for i := 0; i < *n; i++ {
|
||||
parsed := make([]map[string]*ast.Package, *p)
|
||||
@ -105,7 +105,7 @@ func main() {
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
}
|
||||
t1 := time.Nanoseconds()
|
||||
t1 := time.Now()
|
||||
|
||||
fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
|
||||
st.Alloc, st.TotalAlloc,
|
||||
@ -120,7 +120,7 @@ func main() {
|
||||
}
|
||||
*/
|
||||
// Standard gotest benchmark output, collected by build dashboard.
|
||||
gcstats("BenchmarkParser", *n, t1-t0)
|
||||
gcstats("BenchmarkParser", *n, t1.Sub(t0))
|
||||
|
||||
if *serve != "" {
|
||||
log.Fatal(http.ListenAndServe(*serve, nil))
|
||||
@ -130,18 +130,17 @@ func main() {
|
||||
|
||||
var packages = []string{
|
||||
"archive/tar",
|
||||
"asn1",
|
||||
"big",
|
||||
"encoding/asn1",
|
||||
"math/big",
|
||||
"bufio",
|
||||
"bytes",
|
||||
"cmath",
|
||||
"math/cmplx",
|
||||
"compress/flate",
|
||||
"compress/gzip",
|
||||
"compress/zlib",
|
||||
"container/heap",
|
||||
"container/list",
|
||||
"container/ring",
|
||||
"container/vector",
|
||||
"crypto/aes",
|
||||
"crypto/blowfish",
|
||||
"crypto/hmac",
|
||||
@ -161,16 +160,14 @@ var packages = []string{
|
||||
"debug/macho",
|
||||
"debug/elf",
|
||||
"debug/gosym",
|
||||
"ebnf",
|
||||
"exp/ebnf",
|
||||
"encoding/ascii85",
|
||||
"encoding/base64",
|
||||
"encoding/binary",
|
||||
"encoding/git85",
|
||||
"encoding/hex",
|
||||
"encoding/pem",
|
||||
"exec",
|
||||
"exp/datafmt",
|
||||
"expvar",
|
||||
"os/exec",
|
||||
"flag",
|
||||
"fmt",
|
||||
"go/ast",
|
||||
@ -179,18 +176,18 @@ var packages = []string{
|
||||
"go/printer",
|
||||
"go/scanner",
|
||||
"go/token",
|
||||
"gob",
|
||||
"encoding/gob",
|
||||
"hash",
|
||||
"hash/adler32",
|
||||
"hash/crc32",
|
||||
"hash/crc64",
|
||||
"http",
|
||||
"net/http",
|
||||
"image",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"io",
|
||||
"io/ioutil",
|
||||
"json",
|
||||
"encoding/json",
|
||||
"log",
|
||||
"math",
|
||||
"mime",
|
||||
@ -199,29 +196,29 @@ var packages = []string{
|
||||
"os/signal",
|
||||
"patch",
|
||||
"path",
|
||||
"rand",
|
||||
"math/rand",
|
||||
"reflect",
|
||||
"regexp",
|
||||
"rpc",
|
||||
"net/rpc",
|
||||
"runtime",
|
||||
"scanner",
|
||||
"text/scanner",
|
||||
"sort",
|
||||
"smtp",
|
||||
"net/smtp",
|
||||
"strconv",
|
||||
"strings",
|
||||
"sync",
|
||||
"syscall",
|
||||
"syslog",
|
||||
"tabwriter",
|
||||
"template",
|
||||
"log/syslog",
|
||||
"text/tabwriter",
|
||||
"text/template",
|
||||
"testing",
|
||||
"testing/iotest",
|
||||
"testing/quick",
|
||||
"testing/script",
|
||||
"time",
|
||||
"unicode",
|
||||
"utf8",
|
||||
"utf16",
|
||||
"unicode/utf8",
|
||||
"unicode/utf16",
|
||||
"websocket",
|
||||
"xml",
|
||||
"encoding/xml",
|
||||
}
|
||||
|
@ -108,15 +108,14 @@ func verify() {
|
||||
// -------------------------------------
|
||||
// Factorial
|
||||
|
||||
|
||||
func main() {
|
||||
t0 := time.Nanoseconds()
|
||||
t0 := time.Now()
|
||||
verify()
|
||||
for i := 0; i <= 9; i++ {
|
||||
print(i, "! = ", count(fact(gen(i))), "\n")
|
||||
}
|
||||
runtime.GC()
|
||||
t1 := time.Nanoseconds()
|
||||
t1 := time.Now()
|
||||
|
||||
gcstats("BenchmarkPeano", 1, t1-t0)
|
||||
gcstats("BenchmarkPeano", 1, t1.Sub(t0))
|
||||
}
|
||||
|
@ -8,12 +8,13 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
func gcstats(name string, n int, t int64) {
|
||||
func gcstats(name string, n int, t time.Duration) {
|
||||
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.%s %d %d ns/op\n", name, n, t.Nanoseconds()/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)
|
||||
|
@ -68,7 +68,7 @@ const minDepth = 4
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
t0 := time.Nanoseconds()
|
||||
t0 := time.Now()
|
||||
|
||||
maxDepth := *n
|
||||
if minDepth+2 > *n {
|
||||
@ -93,8 +93,8 @@ func main() {
|
||||
}
|
||||
fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, longLivedTree.itemCheck())
|
||||
|
||||
t1 := time.Nanoseconds()
|
||||
t1 := time.Now()
|
||||
|
||||
// Standard gotest benchmark output, collected by build dashboard.
|
||||
gcstats("BenchmarkTree", *n, t1-t0)
|
||||
gcstats("BenchmarkTree", *n, t1.Sub(t0))
|
||||
}
|
||||
|
88
test/bench/garbage/tree2.go
Normal file
88
test/bench/garbage/tree2.go
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2012 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const BranchingFactor = 4
|
||||
|
||||
type Object struct {
|
||||
child [BranchingFactor]*Object
|
||||
}
|
||||
|
||||
var (
|
||||
cpus = flag.Int("cpus", 1, "number of cpus to use")
|
||||
heapsize = flag.Int64("heapsize", 100*1024*1024, "size of the heap in bytes")
|
||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
||||
|
||||
lastPauseNs uint64 = 0
|
||||
lastFree uint64 = 0
|
||||
heap *Object
|
||||
calls [20]int
|
||||
numobjects int64
|
||||
)
|
||||
|
||||
func buildHeap() {
|
||||
objsize := int64(unsafe.Sizeof(Object{}))
|
||||
heap, _ = buildTree(float64(objsize), float64(*heapsize), 0)
|
||||
fmt.Printf("*** built heap: %.0f MB; (%d objects * %d bytes)\n",
|
||||
float64(*heapsize)/1048576, numobjects, objsize)
|
||||
}
|
||||
|
||||
func buildTree(objsize, size float64, depth int) (*Object, float64) {
|
||||
calls[depth]++
|
||||
x := &Object{}
|
||||
numobjects++
|
||||
subtreeSize := (size - objsize) / BranchingFactor
|
||||
alloc := objsize
|
||||
for i := 0; i < BranchingFactor && alloc < size; i++ {
|
||||
c, n := buildTree(objsize, subtreeSize, depth+1)
|
||||
x.child[i] = c
|
||||
alloc += n
|
||||
}
|
||||
return x, alloc
|
||||
}
|
||||
|
||||
func gc() {
|
||||
runtime.GC()
|
||||
runtime.UpdateMemStats()
|
||||
pause := runtime.MemStats.PauseTotalNs
|
||||
inuse := runtime.MemStats.Alloc
|
||||
free := runtime.MemStats.TotalAlloc - inuse
|
||||
fmt.Printf("gc pause: %8.3f ms; collect: %8.0f MB; heapsize: %8.0f MB\n",
|
||||
float64(pause-lastPauseNs)/1e6,
|
||||
float64(free-lastFree)/1048576,
|
||||
float64(inuse)/1048576)
|
||||
lastPauseNs = pause
|
||||
lastFree = free
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
buildHeap()
|
||||
runtime.GOMAXPROCS(*cpus)
|
||||
runtime.UpdateMemStats()
|
||||
lastPauseNs = runtime.MemStats.PauseTotalNs
|
||||
lastFree = runtime.MemStats.TotalAlloc - runtime.MemStats.Alloc
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
gc()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user