github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/_vendor/src/golang.org/x/tools/cmd/compilebench/main.go (about) 1 // Copyright 2015 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 // Compilebench benchmarks the speed of the Go compiler. 6 // 7 // Usage: 8 // 9 // compilebench [options] 10 // 11 // It times the compilation of various packages and prints results in 12 // the format used by package testing (and expected by golang.org/x/perf/cmd/benchstat). 13 // 14 // The options are: 15 // 16 // -alloc 17 // Report allocations. 18 // 19 // -compile exe 20 // Use exe as the path to the cmd/compile binary. 21 // 22 // -compileflags 'list' 23 // Pass the space-separated list of flags to the compilation. 24 // 25 // -count n 26 // Run each benchmark n times (default 1). 27 // 28 // -cpuprofile file 29 // Write a CPU profile of the compiler to file. 30 // 31 // -memprofile file 32 // Write a memory profile of the compiler to file. 33 // 34 // -memprofilerate rate 35 // Set runtime.MemProfileRate during compilation. 36 // 37 // -obj 38 // Report object file statistics. 39 // 40 // -pkg 41 // Benchmark compiling a single package. 42 // 43 // -run regexp 44 // Only run benchmarks with names matching regexp. 45 // 46 // Although -cpuprofile and -memprofile are intended to write a 47 // combined profile for all the executed benchmarks to file, 48 // today they write only the profile for the last benchmark executed. 49 // 50 // The default memory profiling rate is one profile sample per 512 kB 51 // allocated (see ``go doc runtime.MemProfileRate''). 52 // Lowering the rate (for example, -memprofilerate 64000) produces 53 // a more fine-grained and therefore accurate profile, but it also incurs 54 // execution cost. For benchmark comparisons, never use timings 55 // obtained with a low -memprofilerate option. 56 // 57 // Example 58 // 59 // Assuming the base version of the compiler has been saved with 60 // ``toolstash save,'' this sequence compares the old and new compiler: 61 // 62 // compilebench -count 10 -compile $(toolstash -n compile) >old.txt 63 // compilebench -count 10 >new.txt 64 // benchstat old.txt new.txt 65 // 66 package main 67 68 import ( 69 "bytes" 70 "flag" 71 "fmt" 72 "go/build" 73 "io/ioutil" 74 "log" 75 "os" 76 "os/exec" 77 "path/filepath" 78 "regexp" 79 "strconv" 80 "strings" 81 "time" 82 ) 83 84 var ( 85 goroot string 86 compiler string 87 runRE *regexp.Regexp 88 is6g bool 89 ) 90 91 var ( 92 flagGoCmd = flag.String("go", "go", "path to \"go\" command") 93 flagAlloc = flag.Bool("alloc", false, "report allocations") 94 flagObj = flag.Bool("obj", false, "report object file stats") 95 flagCompiler = flag.String("compile", "", "use `exe` as the cmd/compile binary") 96 flagCompilerFlags = flag.String("compileflags", "", "additional `flags` to pass to compile") 97 flagRun = flag.String("run", "", "run benchmarks matching `regexp`") 98 flagCount = flag.Int("count", 1, "run benchmarks `n` times") 99 flagCpuprofile = flag.String("cpuprofile", "", "write CPU profile to `file`") 100 flagMemprofile = flag.String("memprofile", "", "write memory profile to `file`") 101 flagMemprofilerate = flag.Int64("memprofilerate", -1, "set memory profile `rate`") 102 flagPackage = flag.String("pkg", "", "if set, benchmark the package at path `pkg`") 103 flagShort = flag.Bool("short", false, "skip long-running benchmarks") 104 ) 105 106 var tests = []struct { 107 name string 108 dir string 109 long bool 110 }{ 111 {"BenchmarkTemplate", "html/template", false}, 112 {"BenchmarkUnicode", "unicode", false}, 113 {"BenchmarkGoTypes", "go/types", false}, 114 {"BenchmarkCompiler", "cmd/compile/internal/gc", false}, 115 {"BenchmarkSSA", "cmd/compile/internal/ssa", false}, 116 {"BenchmarkFlate", "compress/flate", false}, 117 {"BenchmarkGoParser", "go/parser", false}, 118 {"BenchmarkReflect", "reflect", false}, 119 {"BenchmarkTar", "archive/tar", false}, 120 {"BenchmarkXML", "encoding/xml", false}, 121 {"BenchmarkStdCmd", "", true}, 122 {"BenchmarkHelloSize", "", false}, 123 {"BenchmarkCmdGoSize", "", true}, 124 } 125 126 func usage() { 127 fmt.Fprintf(os.Stderr, "usage: compilebench [options]\n") 128 fmt.Fprintf(os.Stderr, "options:\n") 129 flag.PrintDefaults() 130 os.Exit(2) 131 } 132 133 func main() { 134 log.SetFlags(0) 135 log.SetPrefix("compilebench: ") 136 flag.Usage = usage 137 flag.Parse() 138 if flag.NArg() != 0 { 139 usage() 140 } 141 142 s, err := exec.Command(*flagGoCmd, "env", "GOROOT").CombinedOutput() 143 if err != nil { 144 log.Fatalf("%s env GOROOT: %v", *flagGoCmd, err) 145 } 146 goroot = strings.TrimSpace(string(s)) 147 148 compiler = *flagCompiler 149 if compiler == "" { 150 out, err := exec.Command(*flagGoCmd, "tool", "-n", "compile").CombinedOutput() 151 if err != nil { 152 out, err = exec.Command(*flagGoCmd, "tool", "-n", "6g").CombinedOutput() 153 is6g = true 154 if err != nil { 155 out, err = exec.Command(*flagGoCmd, "tool", "-n", "compile").CombinedOutput() 156 log.Fatalf("go tool -n compiler: %v\n%s", err, out) 157 } 158 } 159 compiler = strings.TrimSpace(string(out)) 160 } 161 162 if *flagRun != "" { 163 r, err := regexp.Compile(*flagRun) 164 if err != nil { 165 log.Fatalf("invalid -run argument: %v", err) 166 } 167 runRE = r 168 } 169 170 for i := 0; i < *flagCount; i++ { 171 if *flagPackage != "" { 172 runBuild("BenchmarkPkg", *flagPackage, i) 173 continue 174 } 175 for _, tt := range tests { 176 if tt.long && *flagShort { 177 continue 178 } 179 if runRE == nil || runRE.MatchString(tt.name) { 180 runBuild(tt.name, tt.dir, i) 181 } 182 } 183 } 184 } 185 186 func runCmd(name string, cmd *exec.Cmd) { 187 start := time.Now() 188 out, err := cmd.CombinedOutput() 189 if err != nil { 190 log.Printf("%v: %v\n%s", name, err, out) 191 return 192 } 193 fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds()) 194 } 195 196 func runStdCmd() { 197 args := []string{"build", "-a"} 198 if *flagCompilerFlags != "" { 199 args = append(args, "-gcflags", *flagCompilerFlags) 200 } 201 args = append(args, "std", "cmd") 202 cmd := exec.Command(*flagGoCmd, args...) 203 cmd.Dir = filepath.Join(goroot, "src") 204 runCmd("BenchmarkStdCmd", cmd) 205 } 206 207 // path is either a path to a file ("$GOROOT/test/helloworld.go") or a package path ("cmd/go"). 208 func runSize(name, path string) { 209 cmd := exec.Command(*flagGoCmd, "build", "-o", "_compilebenchout_", path) 210 cmd.Stdout = os.Stderr 211 cmd.Stderr = os.Stderr 212 if err := cmd.Run(); err != nil { 213 log.Print(err) 214 return 215 } 216 defer os.Remove("_compilebenchout_") 217 info, err := os.Stat("_compilebenchout_") 218 if err != nil { 219 log.Print(err) 220 return 221 } 222 out, err := exec.Command("size", "_compilebenchout_").CombinedOutput() 223 if err != nil { 224 log.Printf("size: %v\n%s", err, out) 225 return 226 } 227 lines := strings.Split(string(out), "\n") 228 if len(lines) < 2 { 229 log.Printf("not enough output from size: %s", out) 230 return 231 } 232 f := strings.Fields(lines[1]) 233 if strings.HasPrefix(lines[0], "__TEXT") && len(f) >= 2 { // OS X 234 fmt.Printf("%s 1 %s text-bytes %s data-bytes %v exe-bytes\n", name, f[0], f[1], info.Size()) 235 } else if strings.Contains(lines[0], "bss") && len(f) >= 3 { 236 fmt.Printf("%s 1 %s text-bytes %s data-bytes %s bss-bytes %v exe-bytes\n", name, f[0], f[1], f[2], info.Size()) 237 } 238 } 239 240 func runBuild(name, dir string, count int) { 241 switch name { 242 case "BenchmarkStdCmd": 243 runStdCmd() 244 return 245 case "BenchmarkCmdGoSize": 246 runSize("BenchmarkCmdGoSize", "cmd/go") 247 return 248 case "BenchmarkHelloSize": 249 runSize("BenchmarkHelloSize", filepath.Join(goroot, "test/helloworld.go")) 250 return 251 } 252 253 pkg, err := build.Import(dir, ".", 0) 254 if err != nil { 255 log.Print(err) 256 return 257 } 258 args := []string{"-o", "_compilebench_.o"} 259 if is6g { 260 *flagMemprofilerate = -1 261 *flagAlloc = false 262 *flagCpuprofile = "" 263 *flagMemprofile = "" 264 } 265 if *flagMemprofilerate >= 0 { 266 args = append(args, "-memprofilerate", fmt.Sprint(*flagMemprofilerate)) 267 } 268 args = append(args, strings.Fields(*flagCompilerFlags)...) 269 if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" { 270 if *flagAlloc || *flagMemprofile != "" { 271 args = append(args, "-memprofile", "_compilebench_.memprof") 272 } 273 if *flagCpuprofile != "" { 274 args = append(args, "-cpuprofile", "_compilebench_.cpuprof") 275 } 276 } 277 args = append(args, pkg.GoFiles...) 278 cmd := exec.Command(compiler, args...) 279 cmd.Dir = pkg.Dir 280 cmd.Stdout = os.Stderr 281 cmd.Stderr = os.Stderr 282 start := time.Now() 283 err = cmd.Run() 284 if err != nil { 285 log.Printf("%v: %v", name, err) 286 return 287 } 288 end := time.Now() 289 290 var allocs, allocbytes int64 291 if *flagAlloc || *flagMemprofile != "" { 292 out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.memprof") 293 if err != nil { 294 log.Print("cannot find memory profile after compilation") 295 } 296 for _, line := range strings.Split(string(out), "\n") { 297 f := strings.Fields(line) 298 if len(f) < 4 || f[0] != "#" || f[2] != "=" { 299 continue 300 } 301 val, err := strconv.ParseInt(f[3], 0, 64) 302 if err != nil { 303 continue 304 } 305 switch f[1] { 306 case "TotalAlloc": 307 allocbytes = val 308 case "Mallocs": 309 allocs = val 310 } 311 } 312 313 if *flagMemprofile != "" { 314 if err := ioutil.WriteFile(*flagMemprofile, out, 0666); err != nil { 315 log.Print(err) 316 } 317 } 318 os.Remove(pkg.Dir + "/_compilebench_.memprof") 319 } 320 321 if *flagCpuprofile != "" { 322 out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.cpuprof") 323 if err != nil { 324 log.Print(err) 325 } 326 outpath := *flagCpuprofile 327 if *flagCount != 1 { 328 outpath = fmt.Sprintf("%s_%d", outpath, count) 329 } 330 if err := ioutil.WriteFile(outpath, out, 0666); err != nil { 331 log.Print(err) 332 } 333 os.Remove(pkg.Dir + "/_compilebench_.cpuprof") 334 } 335 336 wallns := end.Sub(start).Nanoseconds() 337 userns := cmd.ProcessState.UserTime().Nanoseconds() 338 339 fmt.Printf("%s 1 %d ns/op %d user-ns/op", name, wallns, userns) 340 if *flagAlloc { 341 fmt.Printf(" %d B/op %d allocs/op", allocbytes, allocs) 342 } 343 344 opath := pkg.Dir + "/_compilebench_.o" 345 if *flagObj { 346 // TODO(josharian): object files are big; just read enough to find what we seek. 347 data, err := ioutil.ReadFile(opath) 348 if err != nil { 349 log.Print(err) 350 } 351 // Find start of export data. 352 i := bytes.Index(data, []byte("\n$$B\n")) + len("\n$$B\n") 353 // Count bytes to end of export data. 354 nexport := bytes.Index(data[i:], []byte("\n$$\n")) 355 fmt.Printf(" %d object-bytes %d export-bytes", len(data), nexport) 356 } 357 fmt.Println() 358 359 os.Remove(opath) 360 }