1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Garbage collection benchmark: parse Go packages repeatedly.
25 var serve = flag.String("serve", "", "serve http on this address at end")
27 func isGoFile(dir os.FileInfo) bool {
28 return !dir.IsDir() &&
29 !strings.HasPrefix(dir.Name(), ".") && // ignore .files
30 path.Ext(dir.Name()) == ".go"
33 func isPkgFile(dir os.FileInfo) bool {
34 return isGoFile(dir) &&
35 !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
38 func pkgName(filename string) string {
39 file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
40 if err != nil || file == nil {
46 func parseDir(dirpath string) map[string]*ast.Package {
47 // the package name is the directory name within its parent
48 // (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
49 _, pkgname := path.Split(dirpath)
51 // filter function to select the desired .go files
52 filter := func(d os.FileInfo) bool {
54 // Some directories contain main packages: Only accept
55 // files that belong to the expected package so that
56 // parser.ParsePackage doesn't return "multiple packages
58 // Additionally, accept the special package name
59 // fakePkgName if we are looking at cmd documentation.
60 name := pkgName(dirpath + "/" + d.Name())
61 return name == pkgname
67 pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments)
69 println("parse", dirpath, err.Error())
76 st := new(runtime.MemStats)
77 packages = append(packages, packages...)
78 packages = append(packages, packages...)
79 n := flag.Int("n", 4, "iterations")
80 p := flag.Int("p", len(packages), "# of packages to keep in memory")
81 flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
84 var lastParsed []map[string]*ast.Package
87 var pauseTotalNs uint64
88 pkgroot := runtime.GOROOT() + "/src/pkg/"
89 for pass := 0; pass < 2; pass++ {
90 // Once the heap is grown to full size, reset counters.
91 // This hides the start-up pauses, which are much smaller
92 // than the normal pauses and would otherwise make
93 // the average look much better than it actually is.
94 runtime.ReadMemStats(st)
96 pauseTotalNs = st.PauseTotalNs
99 for i := 0; i < *n; i++ {
100 parsed := make([]map[string]*ast.Package, *p)
101 for j := range parsed {
102 parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
104 if i+1 == *n && *serve != "" {
113 runtime.ReadMemStats(st)
115 st.PauseTotalNs -= pauseTotalNs
116 fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
117 st.Alloc, st.TotalAlloc,
119 st.Mallocs, float64(st.PauseTotalNs)/1e9,
120 st.NumGC, float64(st.PauseTotalNs)/1e9/float64(st.NumGC))
123 fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
124 for _, s := range st.BySize {
125 fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
128 // Standard gotest benchmark output, collected by build dashboard.
129 gcstats("BenchmarkParser", *n, t1.Sub(t0))
132 log.Fatal(http.ListenAndServe(*serve, nil))
137 var packages = []string{