]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: instrument malloc + garbage collector.
authorRuss Cox <rsc@golang.org>
Mon, 8 Feb 2010 22:32:22 +0000 (14:32 -0800)
committerRuss Cox <rsc@golang.org>
Mon, 8 Feb 2010 22:32:22 +0000 (14:32 -0800)
add simple garbage collection benchmark.

R=iant
CC=golang-dev
https://golang.org/cl/204053

14 files changed:
src/pkg/runtime/darwin/386/sys.s
src/pkg/runtime/darwin/amd64/sys.s
src/pkg/runtime/extern.go
src/pkg/runtime/freebsd/386/sys.s
src/pkg/runtime/freebsd/amd64/sys.s
src/pkg/runtime/linux/386/sys.s
src/pkg/runtime/linux/amd64/sys.s
src/pkg/runtime/malloc.cgo
src/pkg/runtime/malloc.h
src/pkg/runtime/mgc0.c
src/pkg/runtime/msize.c
src/pkg/runtime/runtime.c
src/pkg/runtime/runtime.h
test/garbage/parser.go [new file with mode: 0644]

index 326cc23e5245d84f52540725ae5f5b558429f739..79628a463f02a0661cee98f96b93b547ae0a954b 100644 (file)
@@ -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
index 223790a523e09ea964923dc16e6751058a218df2..50b50d5de07f67e3282a4261d6c1ed80d8cc92d5 100644 (file)
@@ -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
-
index a397c3b90576a77f647a04daa8d2d3c07e797769..0834f78791a3c7c27304f8eb07962ba72854f4ce 100644 (file)
@@ -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.
index d0afeae3a9e7335a83b0a3c8581f0e7c27b1faf6..a0860db81b4d5b1fbb7f9e47d2a4ba957d3ab6c9 100644 (file)
@@ -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
index 53773b99f36d15d0c59f8513ef5b3ad62d5d31ec..02c3e91436bbc5280b2abe9ad16ac21f493bd5c5 100644 (file)
@@ -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
index 7f644cbf3f71fefbbc8e6a86e0a2426a7257bdec..ed7c155f1f507b14c4baa50cfe60fac3879ea617 100644 (file)
@@ -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
index 8e0905ee15b63e4764e616d5d86f9bd2bde52f7e..18bf5b50920ab171324b08aa82ad1c1f3ef01e3b 100644 (file)
@@ -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
index d7e3e4151dfd47d679d9cbcdd7c249b985fc0b73..286aa2bf3c0a860cfd87b464deeaa4c918902597 100644 (file)
@@ -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:
index 133ed023296320332278214394ed979199c5aa4c..05f500a1e7db785320b42ef0b677fb18989cc951 100644 (file)
@@ -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 */
index d8a943e2a23aa9afe5778b9d63bc0ab8977c9ac0..83d217320d4564c62e7765ba510a0c290d7b3745 100644 (file)
@@ -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();
 }
index 25e22637d2852de332c738ca29bbe794b0a50b17..aebc15416dba9a31f36e0f0e2693c21e000f9594 100644 (file)
@@ -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];
index 2d840aab37b85275f6eab40bf704767c65306ed2..3a94c8bce26f3214cf37a6cc927ec00c383e5197 100644 (file)
@@ -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;
+}
index 2d6d42ee62796c7df71e9ab0a61f89d9fbe45e06..a526c04927c258620e6fe9b177fa4de3d9cc63d1 100644 (file)
@@ -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 (file)
index 0000000..3a21f97
--- /dev/null
@@ -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",
+}