// 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" "go/token" "log" "net/http" _ "net/http/pprof" "os" "path" "runtime" "strings" "time" ) var serve = flag.String("serve", "", "serve http on this address at end") 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 { return isGoFile(dir) && !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files } func pkgName(filename string) string { file, err := parser.ParseFile(token.NewFileSet(), filename, 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.FileInfo) 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(token.NewFileSet(), dirpath, filter, parser.ParseComments) if err != nil { println("parse", dirpath, err.Error()) panic("fail") } return pkgs } func main() { st := new(runtime.MemStats) packages = append(packages, packages...) packages = append(packages, packages...) n := flag.Int("n", 4, "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() var lastParsed []map[string]*ast.Package var t0 time.Time var numGC uint32 var pauseTotalNs uint64 pkgroot := runtime.GOROOT() + "/src/pkg/" for pass := 0; pass < 2; pass++ { // Once the 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. runtime.ReadMemStats(st) numGC = st.NumGC pauseTotalNs = st.PauseTotalNs t0 = time.Now() for i := 0; 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 == *n && *serve != "" { lastParsed = parsed } } runtime.GC() runtime.GC() } t1 := time.Now() runtime.ReadMemStats(st) st.NumGC -= numGC st.PauseTotalNs -= pauseTotalNs fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n", st.Alloc, st.TotalAlloc, st.Sys, st.Mallocs, float64(st.PauseTotalNs)/1e9, 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) } */ // Standard gotest benchmark output, collected by build dashboard. gcstats("BenchmarkParser", *n, t1.Sub(t0)) if *serve != "" { log.Fatal(http.ListenAndServe(*serve, nil)) println(lastParsed) } } var packages = []string{ "archive/tar", "encoding/asn1", "math/big", "bufio", "bytes", "math/cmplx", "compress/flate", "compress/gzip", "compress/zlib", "container/heap", "container/list", "container/ring", "crypto/aes", "crypto/hmac", "crypto/md5", "crypto/rand", "crypto/rc4", "crypto/rsa", "crypto/sha1", "crypto/sha256", "crypto/sha512", "crypto/subtle", "crypto/tls", "crypto/x509", "debug/dwarf", "debug/macho", "debug/elf", "debug/gosym", "exp/ebnf", "encoding/ascii85", "encoding/base64", "encoding/binary", "encoding/hex", "encoding/pem", "os/exec", "flag", "fmt", "go/ast", "go/doc", "go/parser", "go/printer", "go/scanner", "go/token", "encoding/gob", "hash", "hash/adler32", "hash/crc32", "hash/crc64", "net/http", "image", "image/jpeg", "image/png", "io", "io/ioutil", "encoding/json", "log", "math", "mime", "net", "os", "exp/signal", "path", "math/rand", "reflect", "regexp", "net/rpc", "runtime", "text/scanner", "sort", "net/smtp", "strconv", "strings", "sync", "syscall", "log/syslog", "text/tabwriter", "text/template", "testing", "testing/iotest", "testing/quick", "testing/script", "time", "unicode", "unicode/utf8", "unicode/utf16", "encoding/xml", }