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.
4
5// Garbage collection benchmark: parse Go packages repeatedly.
6
7package main
8
9import (
10	"flag"
11	"fmt"
12	"go/ast"
13	"go/parser"
14	"go/token"
15	"log"
16	"net/http"
17	_ "net/http/pprof"
18	"os"
19	"path"
20	"runtime"
21	"strings"
22	"time"
23)
24
25var serve = flag.String("serve", "", "serve http on this address at end")
26
27func isGoFile(dir os.FileInfo) bool {
28	return !dir.IsDir() &&
29		!strings.HasPrefix(dir.Name(), ".") && // ignore .files
30		path.Ext(dir.Name()) == ".go"
31}
32
33func isPkgFile(dir os.FileInfo) bool {
34	return isGoFile(dir) &&
35		!strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
36}
37
38func pkgName(filename string) string {
39	file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
40	if err != nil || file == nil {
41		return ""
42	}
43	return file.Name.Name
44}
45
46func 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)
50
51	// filter function to select the desired .go files
52	filter := func(d os.FileInfo) bool {
53		if isPkgFile(d) {
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
57			// found" errors.
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
62		}
63		return false
64	}
65
66	// get package AST
67	pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments)
68	if err != nil {
69		println("parse", dirpath, err.Error())
70		panic("fail")
71	}
72	return pkgs
73}
74
75func main() {
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)")
82	flag.Parse()
83
84	var lastParsed []map[string]*ast.Package
85	var t0 time.Time
86	var numGC uint32
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)
95		numGC = st.NumGC
96		pauseTotalNs = st.PauseTotalNs
97		t0 = time.Now()
98
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)])
103			}
104			if i+1 == *n && *serve != "" {
105				lastParsed = parsed
106			}
107		}
108		runtime.GC()
109		runtime.GC()
110	}
111	t1 := time.Now()
112
113	runtime.ReadMemStats(st)
114	st.NumGC -= numGC
115	st.PauseTotalNs -= pauseTotalNs
116	fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
117		st.Alloc, st.TotalAlloc,
118		st.Sys,
119		st.Mallocs, float64(st.PauseTotalNs)/1e9,
120		st.NumGC, float64(st.PauseTotalNs)/1e9/float64(st.NumGC))
121
122	/*
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)
126		}
127	*/
128	// Standard gotest benchmark output, collected by build dashboard.
129	gcstats("BenchmarkParser", *n, t1.Sub(t0))
130
131	if *serve != "" {
132		log.Fatal(http.ListenAndServe(*serve, nil))
133		println(lastParsed)
134	}
135}
136
137// find . -type d -not -path "./exp" -not -path "./exp/*" -printf "\t\"%p\",\n" | sort | sed "s/\.\///" | grep -v testdata
138var packages = []string{
139	"archive",
140	"archive/tar",
141	"archive/zip",
142	"bufio",
143	"builtin",
144	"bytes",
145	"compress",
146	"compress/bzip2",
147	"compress/flate",
148	"compress/gzip",
149	"compress/lzw",
150	"compress/zlib",
151	"container",
152	"container/heap",
153	"container/list",
154	"container/ring",
155	"crypto",
156	"crypto/aes",
157	"crypto/cipher",
158	"crypto/des",
159	"crypto/dsa",
160	"crypto/ecdsa",
161	"crypto/elliptic",
162	"crypto/hmac",
163	"crypto/md5",
164	"crypto/rand",
165	"crypto/rc4",
166	"crypto/rsa",
167	"crypto/sha1",
168	"crypto/sha256",
169	"crypto/sha512",
170	"crypto/subtle",
171	"crypto/tls",
172	"crypto/x509",
173	"crypto/x509/pkix",
174	"database",
175	"database/sql",
176	"database/sql/driver",
177	"debug",
178	"debug/dwarf",
179	"debug/elf",
180	"debug/gosym",
181	"debug/macho",
182	"debug/pe",
183	"encoding",
184	"encoding/ascii85",
185	"encoding/asn1",
186	"encoding/base32",
187	"encoding/base64",
188	"encoding/binary",
189	"encoding/csv",
190	"encoding/gob",
191	"encoding/hex",
192	"encoding/json",
193	"encoding/pem",
194	"encoding/xml",
195	"errors",
196	"expvar",
197	"flag",
198	"fmt",
199	"go",
200	"go/ast",
201	"go/build",
202	"go/doc",
203	"go/format",
204	"go/parser",
205	"go/printer",
206	"go/scanner",
207	"go/token",
208	"hash",
209	"hash/adler32",
210	"hash/crc32",
211	"hash/crc64",
212	"hash/fnv",
213	"html",
214	"html/template",
215	"image",
216	"image/color",
217	"image/draw",
218	"image/gif",
219	"image/jpeg",
220	"image/png",
221	"index",
222	"index/suffixarray",
223	"io",
224	"io/ioutil",
225	"log",
226	"log/syslog",
227	"math",
228	"math/big",
229	"math/cmplx",
230	"math/rand",
231	"mime",
232	"mime/multipart",
233	"net",
234	"net/http",
235	"net/http/cgi",
236	"net/http/cookiejar",
237	"net/http/fcgi",
238	"net/http/httptest",
239	"net/http/httputil",
240	"net/http/pprof",
241	"net/mail",
242	"net/rpc",
243	"net/rpc/jsonrpc",
244	"net/smtp",
245	"net/textproto",
246	"net/url",
247	"os",
248	"os/exec",
249	"os/signal",
250	"os/user",
251	"path",
252	"path/filepath",
253	"reflect",
254	"regexp",
255	"regexp/syntax",
256	"runtime",
257	"runtime/cgo",
258	"runtime/debug",
259	"runtime/pprof",
260	"runtime/race",
261	"sort",
262	"strconv",
263	"strings",
264	"sync",
265	"sync/atomic",
266	"syscall",
267	"testing",
268	"testing/iotest",
269	"testing/quick",
270	"text",
271	"text/scanner",
272	"text/tabwriter",
273	"text/template",
274	"text/template/parse",
275	"time",
276	"unicode",
277	"unicode/utf16",
278	"unicode/utf8",
279	"unsafe",
280}
281