summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2010-02-08 14:32:22 -0800
committerRuss Cox <rsc@golang.org>2010-02-08 14:32:22 -0800
commitefc07e53e25a0bdbcf9564b8582d0f14206caf69 (patch)
treeb0c44e6fe88273223814fc86db6ad41e74039974
parent2ad0035daa6c682ebfd681288a87940d06fd45d5 (diff)
downloadgolang-efc07e53e25a0bdbcf9564b8582d0f14206caf69.tar.gz
runtime: instrument malloc + garbage collector.
add simple garbage collection benchmark. R=iant CC=golang-dev http://codereview.appspot.com/204053
-rw-r--r--src/pkg/runtime/darwin/386/sys.s16
-rw-r--r--src/pkg/runtime/darwin/amd64/sys.s13
-rw-r--r--src/pkg/runtime/extern.go9
-rw-r--r--src/pkg/runtime/freebsd/386/sys.s17
-rw-r--r--src/pkg/runtime/freebsd/amd64/sys.s15
-rw-r--r--src/pkg/runtime/linux/386/sys.s17
-rw-r--r--src/pkg/runtime/linux/amd64/sys.s15
-rw-r--r--src/pkg/runtime/malloc.cgo4
-rw-r--r--src/pkg/runtime/malloc.h11
-rw-r--r--src/pkg/runtime/mgc0.c7
-rw-r--r--src/pkg/runtime/msize.c4
-rw-r--r--src/pkg/runtime/runtime.c11
-rw-r--r--src/pkg/runtime/runtime.h2
-rw-r--r--test/garbage/parser.go201
14 files changed, 340 insertions, 2 deletions
diff --git a/src/pkg/runtime/darwin/386/sys.s b/src/pkg/runtime/darwin/386/sys.s
index 326cc23e5..79628a463 100644
--- a/src/pkg/runtime/darwin/386/sys.s
+++ b/src/pkg/runtime/darwin/386/sys.s
@@ -42,6 +42,22 @@ TEXT ·mmap(SB),7,$0
CALL notok(SB)
RET
+// void gettime(int64 *sec, int32 *usec)
+TEXT gettime(SB), 7, $32
+ LEAL 12(SP), AX // must be non-nil, unused
+ MOVL AX, 4(SP)
+ MOVL $0, 8(SP) // time zone pointer
+ MOVL $116, AX
+ INT $0x80
+
+ MOVL sec+0(FP), DI
+ MOVL AX, (DI)
+ MOVL $0, 4(DI) // zero extend 32 -> 64
+
+ MOVL usec+4(FP), DI
+ MOVL DX, (DI)
+ RET
+
TEXT sigaction(SB),7,$0
MOVL $46, AX
INT $0x80
diff --git a/src/pkg/runtime/darwin/amd64/sys.s b/src/pkg/runtime/darwin/amd64/sys.s
index 223790a52..50b50d5de 100644
--- a/src/pkg/runtime/darwin/amd64/sys.s
+++ b/src/pkg/runtime/darwin/amd64/sys.s
@@ -37,6 +37,18 @@ TEXT write(SB),7,$0
CALL notok(SB)
RET
+// void gettime(int64 *sec, int32 *usec)
+TEXT gettime(SB), 7, $32
+ MOVQ SP, DI // must be non-nil, unused
+ MOVQ $0, SI
+ MOVQ $(0x2000000+116), AX
+ SYSCALL
+ MOVQ sec+0(FP), DI
+ MOVQ AX, (DI)
+ MOVQ usec+8(FP), DI
+ MOVL DX, (DI)
+ RET
+
TEXT sigaction(SB),7,$0
MOVL 8(SP), DI // arg 1 sig
MOVQ 16(SP), SI // arg 2 act
@@ -226,4 +238,3 @@ TEXT mach_semaphore_signal_all(SB),7,$0
MOVL $(0x1000000+34), AX // semaphore_signal_all_trap
SYSCALL
RET
-
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index a397c3b90..0834f7879 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -73,13 +73,22 @@ func Siginit()
type MemStatsType struct {
Alloc uint64
+ TotalAlloc uint64
Sys uint64
Stacks uint64
InusePages uint64
NextGC uint64
Lookups uint64
Mallocs uint64
+ PauseNs uint64
+ NumGC uint32
EnableGC bool
+ DebugGC bool
+ BySize [67]struct {
+ Size uint32
+ Mallocs uint64
+ Frees uint64
+ }
}
// MemStats holds statistics about the memory system.
diff --git a/src/pkg/runtime/freebsd/386/sys.s b/src/pkg/runtime/freebsd/386/sys.s
index d0afeae3a..a0860db81 100644
--- a/src/pkg/runtime/freebsd/386/sys.s
+++ b/src/pkg/runtime/freebsd/386/sys.s
@@ -73,6 +73,23 @@ TEXT ·mmap(SB),7,$-4
CALL notok(SB)
RET
+TEXT gettime(SB), 7, $32
+ MOVL $116, AX
+ LEAL 12(SP), BX
+ MOVL BX, 4(SP)
+ MOVL $0, 8(SP)
+ INT $0x80
+
+ MOVL 12(SP), BX // sec
+ MOVL sec+0(FP), DI
+ MOVL BX, (DI)
+ MOVL $0, 4(DI) // zero extend 32 -> 64 bits
+
+ MOVL 16(SP), BX // usec
+ MOVL usec+4(FP), DI
+ MOVL BX, (DI)
+ RET
+
TEXT sigaction(SB),7,$-4
MOVL $416, AX
INT $0x80
diff --git a/src/pkg/runtime/freebsd/amd64/sys.s b/src/pkg/runtime/freebsd/amd64/sys.s
index 53773b99f..02c3e9143 100644
--- a/src/pkg/runtime/freebsd/amd64/sys.s
+++ b/src/pkg/runtime/freebsd/amd64/sys.s
@@ -58,6 +58,21 @@ TEXT write(SB),7,$-8
CALL notok(SB)
RET
+TEXT gettime(SB), 7, $32
+ MOVL $116, AX
+ LEAQ 8(SP), DI
+ SYSCALL
+
+ MOVQ 8(SP), BX // sec
+ MOVQ sec+0(FP), DI
+ MOVQ BX, (DI)
+
+ MOVL 16(SP), BX // usec
+ MOVQ usec+8(FP), DI
+ MOVL BX, (DI)
+ RET
+
+
TEXT sigaction(SB),7,$-8
MOVL 8(SP), DI // arg 1 sig
MOVQ 16(SP), SI // arg 2 act
diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s
index 7f644cbf3..ed7c155f1 100644
--- a/src/pkg/runtime/linux/386/sys.s
+++ b/src/pkg/runtime/linux/386/sys.s
@@ -30,6 +30,23 @@ TEXT write(SB),7,$0
INT $0x80
RET
+TEXT gettime(SB), 7, $32
+ MOVL $78, AX // syscall - gettimeofday
+ LEAL 8(SP), BX
+ MOVL $0, CX
+ MOVL $0, DX
+ INT $0x80
+
+ MOVL 8(SP), BX // sec
+ MOVL sec+0(FP), DI
+ MOVL BX, (DI)
+ MOVL $0, 4(DI) // zero extend 32 -> 64 bits
+
+ MOVL 12(SP), BX // usec
+ MOVL usec+4(FP), DI
+ MOVL BX, (DI)
+ RET
+
TEXT rt_sigaction(SB),7,$0
MOVL $174, AX // syscall - rt_sigaction
MOVL 4(SP), BX
diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s
index 8e0905ee1..18bf5b509 100644
--- a/src/pkg/runtime/linux/amd64/sys.s
+++ b/src/pkg/runtime/linux/amd64/sys.s
@@ -44,6 +44,21 @@ TEXT ·write(SB),7,$0-24
SYSCALL
RET
+TEXT gettime(SB), 7, $32
+ LEAQ 8(SP), DI
+ MOVQ $0, SI
+ MOVQ $0xffffffffff600000, AX
+ CALL AX
+
+ MOVQ 8(SP), BX // sec
+ MOVQ sec+0(FP), DI
+ MOVQ BX, (DI)
+
+ MOVL 16(SP), BX // usec
+ MOVQ usec+8(FP), DI
+ MOVL BX, (DI)
+ RET
+
TEXT rt_sigaction(SB),7,$0-32
MOVL 8(SP), DI
MOVQ 16(SP), SI
diff --git a/src/pkg/runtime/malloc.cgo b/src/pkg/runtime/malloc.cgo
index d7e3e4151..286aa2bf3 100644
--- a/src/pkg/runtime/malloc.cgo
+++ b/src/pkg/runtime/malloc.cgo
@@ -46,6 +46,8 @@ mallocgc(uintptr size, uint32 refflag, int32 dogc)
if(v == nil)
throw("out of memory");
mstats.alloc += size;
+ mstats.total_alloc += size;
+ mstats.by_size[sizeclass].nmalloc++;
} else {
// TODO(rsc): Report tracebacks for very large allocations.
@@ -57,6 +59,7 @@ mallocgc(uintptr size, uint32 refflag, int32 dogc)
if(s == nil)
throw("out of memory");
mstats.alloc += npages<<PageShift;
+ mstats.total_alloc += npages<<PageShift;
v = (void*)(s->start << PageShift);
}
@@ -127,6 +130,7 @@ free(void *v)
size = class_to_size[sizeclass];
runtime_memclr(v, size);
mstats.alloc -= size;
+ mstats.by_size[sizeclass].nfree++;
MCache_Free(c, v, sizeclass, size);
out:
diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h
index 133ed0232..05f500a1e 100644
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -156,17 +156,26 @@ void FixAlloc_Free(FixAlloc *f, void *p);
// Statistics.
-// Shared with Go: if you edit this structure, also edit ../malloc/malloc.go.
+// Shared with Go: if you edit this structure, also edit extern.go.
struct MStats
{
uint64 alloc;
+ uint64 total_alloc;
uint64 sys;
uint64 stacks;
uint64 inuse_pages; // protected by mheap.Lock
uint64 next_gc; // protected by mheap.Lock
uint64 nlookup; // unprotected (approximate)
uint64 nmalloc; // unprotected (approximate)
+ uint64 pause_ns;
+ uint32 numgc;
bool enablegc;
+ bool debuggc;
+ struct {
+ uint32 size;
+ uint64 nmalloc;
+ uint64 nfree;
+ } by_size[NumSizeClasses];
};
#define mstats ·MemStats /* name shared with Go */
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index d8a943e2a..83d217320 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -240,6 +240,7 @@ static int32 gcpercent = -2;
void
gc(int32 force)
{
+ int64 t0, t1;
byte *p;
void **fp;
@@ -268,6 +269,7 @@ gc(int32 force)
//printf("gc...\n");
semacquire(&gcsema);
+ t0 = nanotime();
m->gcing = 1;
stoptheworld();
if(mheap.Lock.key != 0)
@@ -289,6 +291,11 @@ gc(int32 force)
pfinq = finq;
m->locks--;
+ t1 = nanotime();
+ mstats.numgc++;
+ mstats.pause_ns += t1 - t0;
+ if(mstats.debuggc)
+ printf("pause %D\n", t1-t0);
semrelease(&gcsema);
starttheworld();
}
diff --git a/src/pkg/runtime/msize.c b/src/pkg/runtime/msize.c
index 25e22637d..aebc15416 100644
--- a/src/pkg/runtime/msize.c
+++ b/src/pkg/runtime/msize.c
@@ -134,6 +134,10 @@ InitSizes(void)
}
}
+ // Copy out for statistics table.
+ for(i=0; i<nelem(class_to_size); i++)
+ mstats.by_size[i].size = class_to_size[i];
+
// Initialize the class_to_transfercount table.
for(sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) {
n = 64*1024 / class_to_size[sizeclass];
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index 2d840aab3..3a94c8bce 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -461,3 +461,14 @@ FLUSH(void *v)
USED(v);
}
+int64
+nanotime(void)
+{
+ int64 sec;
+ int32 usec;
+
+ sec = 0;
+ usec = 0;
+ gettime(&sec, &usec);
+ return sec*1000000000 + (int64)usec*1000;
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 2d6d42ee6..a526c0492 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -393,6 +393,8 @@ void ·exitsyscall(void);
void ·newproc(int32, byte*, byte*);
void siginit(void);
bool sigsend(int32 sig);
+void gettime(int64*, int32*);
+int64 nanotime(void);
#pragma varargck argpos printf 1
diff --git a/test/garbage/parser.go b/test/garbage/parser.go
new file mode 100644
index 000000000..3a21f97a8
--- /dev/null
+++ b/test/garbage/parser.go
@@ -0,0 +1,201 @@
+// 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.
+
+// Garbage collection benchmark: parse Go packages repeatedly.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "os"
+ "path"
+ "runtime"
+ "strings"
+)
+
+func isGoFile(dir *os.Dir) bool {
+ return dir.IsRegular() &&
+ !strings.HasPrefix(dir.Name, ".") && // ignore .files
+ path.Ext(dir.Name) == ".go"
+}
+
+func isPkgFile(dir *os.Dir) bool {
+ return isGoFile(dir) &&
+ !strings.HasSuffix(dir.Name, "_test.go") // ignore test files
+}
+
+func pkgName(filename string) string {
+ file, err := parser.ParseFile(filename, nil, nil, parser.PackageClauseOnly)
+ if err != nil || file == nil {
+ return ""
+ }
+ return file.Name.Name()
+}
+
+func parseDir(dirpath string) map[string]*ast.Package {
+ // the package name is the directory name within its parent
+ // (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
+ _, pkgname := path.Split(dirpath)
+
+ // filter function to select the desired .go files
+ filter := func(d *os.Dir) bool {
+ if isPkgFile(d) {
+ // Some directories contain main packages: Only accept
+ // files that belong to the expected package so that
+ // parser.ParsePackage doesn't return "multiple packages
+ // found" errors.
+ // Additionally, accept the special package name
+ // fakePkgName if we are looking at cmd documentation.
+ name := pkgName(dirpath + "/" + d.Name)
+ return name == pkgname
+ }
+ return false
+ }
+
+ // get package AST
+ pkgs, err := parser.ParseDir(dirpath, filter, parser.ParseComments)
+ if err != nil {
+ panicln("parse", dirpath, err.String())
+ }
+ return pkgs
+}
+
+func main() {
+ st := &runtime.MemStats
+ n := flag.Int("n", 10, "iterations")
+ 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.Parse()
+
+ pkgroot := os.Getenv("GOROOT") + "/src/pkg/"
+ for i := -1; i < *n; i++ {
+ parsed := make([]map[string]*ast.Package, *p)
+ for j := range parsed {
+ parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
+ }
+ if i == -1 {
+ // Now that heap is grown to full size, reset counters.
+ // This hides the start-up pauses, which are much smaller
+ // than the normal pauses and would otherwise make
+ // the average look much better than it actually is.
+ st.NumGC = 0
+ st.PauseNs = 0
+ }
+ }
+
+ fmt.Printf("Alloc=%d/%d Heap=%d/%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
+ st.Alloc, st.TotalAlloc,
+ st.InusePages<<12, st.Sys,
+ st.Mallocs, float64(st.PauseNs)/1e9,
+ st.NumGC, float64(st.PauseNs)/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)
+ }
+}
+
+
+var packages = []string{
+ "archive/tar",
+ "asn1",
+ "big",
+ "bignum",
+ "bufio",
+ "bytes",
+ "compress/flate",
+ "compress/gzip",
+ "compress/zlib",
+ "container/heap",
+ "container/list",
+ "container/ring",
+ "container/vector",
+ "crypto/aes",
+ "crypto/block",
+ "crypto/hmac",
+ "crypto/md4",
+ "crypto/md5",
+ "crypto/rc4",
+ "crypto/rsa",
+ "crypto/sha1",
+ "crypto/sha256",
+ "crypto/subtle",
+ "crypto/tls",
+ "crypto/x509",
+ "crypto/xtea",
+ "debug/dwarf",
+ "debug/macho",
+ "debug/elf",
+ "debug/gosym",
+ "debug/proc",
+ "ebnf",
+ "encoding/ascii85",
+ "encoding/base64",
+ "encoding/binary",
+ "encoding/git85",
+ "encoding/hex",
+ "encoding/pem",
+ "exec",
+ "exp/datafmt",
+ "exp/draw",
+ "exp/eval",
+ "exp/exception",
+ "exp/iterable",
+ "exp/parser",
+ "expvar",
+ "flag",
+ "fmt",
+ "go/ast",
+ "go/doc",
+ "go/parser",
+ "go/printer",
+ "go/scanner",
+ "go/token",
+ "gob",
+ "hash",
+ "hash/adler32",
+ "hash/crc32",
+ "http",
+ "image",
+ "image/jpeg",
+ "image/png",
+ "io",
+ "io/ioutil",
+ "json",
+ "log",
+ "math",
+ "net",
+ "once",
+ "os",
+ "os/signal",
+ "patch",
+ "path",
+ "rand",
+ "reflect",
+ "regexp",
+ "rpc",
+ "runtime",
+ "scanner",
+ "sort",
+ "strconv",
+ "strings",
+ "sync",
+ "syscall",
+ "syslog",
+ "tabwriter",
+ "template",
+ "testing",
+ "testing/iotest",
+ "testing/quick",
+ "testing/script",
+ "time",
+ "unicode",
+ "utf8",
+ "websocket",
+ "xgb",
+ "xml",
+}