cmd/compilebench: import from rsc.io
This CL is an import of compilebench from rsc.io/compilebench at commit 1a5bec7724500bd6df47e053dbc42b41fe653372. This CL brings in only main.go, unaltered. The other files were: * LICENSE: now unnecessary * README.md: now unnecessary * compilecmp: still available at its original home, soon to be superceded by a new tool in x/tools Once compilecmp is available in x/tools, I will replace rsc.io/compilebench with a breadcrumb repo. All outstanding pull requests against rsc.io/compilebench are from me. They will be closed, and new CLs sent here as appropriate. Change-Id: Ic436abf4a662173c6c184bc765b1b9cab13b3cfb Reviewed-on: https://go-review.googlesource.com/39712 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
5054e612af
commit
a0d14bb1f7
|
@ -0,0 +1,319 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// Compilebench benchmarks the speed of the Go compiler.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// compilebench [options]
|
||||
//
|
||||
// It times the compilation of various packages and prints results in
|
||||
// the format used by package testing (and expected by rsc.io/benchstat).
|
||||
//
|
||||
// The options are:
|
||||
//
|
||||
// -alloc
|
||||
// Report allocations.
|
||||
//
|
||||
// -compile exe
|
||||
// Use exe as the path to the cmd/compile binary.
|
||||
//
|
||||
// -compileflags 'list'
|
||||
// Pass the space-separated list of flags to the compilation.
|
||||
//
|
||||
// -count n
|
||||
// Run each benchmark n times (default 1).
|
||||
//
|
||||
// -cpuprofile file
|
||||
// Write a CPU profile of the compiler to file.
|
||||
//
|
||||
// -memprofile file
|
||||
// Write a memory profile of the compiler to file.
|
||||
//
|
||||
// -memprofilerate rate
|
||||
// Set runtime.MemProfileRate during compilation.
|
||||
//
|
||||
// -run regexp
|
||||
// Only run benchmarks with names matching regexp.
|
||||
//
|
||||
// Although -cpuprofile and -memprofile are intended to write a
|
||||
// combined profile for all the executed benchmarks to file,
|
||||
// today they write only the profile for the last benchmark executed.
|
||||
//
|
||||
// The default memory profiling rate is one profile sample per 512 kB
|
||||
// allocated (see ``go doc runtime.MemProfileRate'').
|
||||
// Lowering the rate (for example, -memprofilerate 64000) produces
|
||||
// a more fine-grained and therefore accurate profile, but it also incurs
|
||||
// execution cost. For benchmark comparisons, never use timings
|
||||
// obtained with a low -memprofilerate option.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// Assuming the base version of the compiler has been saved with
|
||||
// ``toolstash save,'' this sequence compares the old and new compiler:
|
||||
//
|
||||
// compilebench -count 10 -compile $(toolstash -n compile) >old.txt
|
||||
// compilebench -count 10 >new.txt
|
||||
// benchstat old.txt new.txt
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
goroot = runtime.GOROOT()
|
||||
compiler string
|
||||
runRE *regexp.Regexp
|
||||
is6g bool
|
||||
)
|
||||
|
||||
var (
|
||||
flagAlloc = flag.Bool("alloc", false, "report allocations")
|
||||
flagCompiler = flag.String("compile", "", "use `exe` as the cmd/compile binary")
|
||||
flagCompilerFlags = flag.String("compileflags", "", "additional `flags` to pass to compile")
|
||||
flagRun = flag.String("run", "", "run benchmarks matching `regexp`")
|
||||
flagCount = flag.Int("count", 1, "run benchmarks `n` times")
|
||||
flagCpuprofile = flag.String("cpuprofile", "", "write CPU profile to `file`")
|
||||
flagMemprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
||||
flagMemprofilerate = flag.Int64("memprofilerate", -1, "set memory profile `rate`")
|
||||
flagShort = flag.Bool("short", false, "skip long-running benchmarks")
|
||||
)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
dir string
|
||||
long bool
|
||||
}{
|
||||
{"BenchmarkTemplate", "html/template", false},
|
||||
{"BenchmarkUnicode", "unicode", false},
|
||||
{"BenchmarkGoTypes", "go/types", false},
|
||||
{"BenchmarkCompiler", "cmd/compile/internal/gc", false},
|
||||
{"BenchmarkMakeBash", "", true},
|
||||
{"BenchmarkHelloSize", "", false},
|
||||
{"BenchmarkCmdGoSize", "", true},
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: compilebench [options]\n")
|
||||
fmt.Fprintf(os.Stderr, "options:\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("compilebench: ")
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if flag.NArg() != 0 {
|
||||
usage()
|
||||
}
|
||||
|
||||
compiler = *flagCompiler
|
||||
if compiler == "" {
|
||||
out, err := exec.Command("go", "tool", "-n", "compile").CombinedOutput()
|
||||
if err != nil {
|
||||
out, err = exec.Command("go", "tool", "-n", "6g").CombinedOutput()
|
||||
is6g = true
|
||||
if err != nil {
|
||||
out, err = exec.Command("go", "tool", "-n", "compile").CombinedOutput()
|
||||
log.Fatalf("go tool -n compiler: %v\n%s", err, out)
|
||||
}
|
||||
}
|
||||
compiler = strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
if *flagRun != "" {
|
||||
r, err := regexp.Compile(*flagRun)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid -run argument: %v", err)
|
||||
}
|
||||
runRE = r
|
||||
}
|
||||
|
||||
for i := 0; i < *flagCount; i++ {
|
||||
for _, tt := range tests {
|
||||
if tt.long && *flagShort {
|
||||
continue
|
||||
}
|
||||
if runRE == nil || runRE.MatchString(tt.name) {
|
||||
runBuild(tt.name, tt.dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCmd(name string, cmd *exec.Cmd) {
|
||||
start := time.Now()
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("%v: %v\n%s", name, err, out)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds())
|
||||
}
|
||||
|
||||
func runMakeBash() {
|
||||
cmd := exec.Command("./make.bash")
|
||||
cmd.Dir = filepath.Join(runtime.GOROOT(), "src")
|
||||
runCmd("BenchmarkMakeBash", cmd)
|
||||
}
|
||||
|
||||
func runCmdGoSize() {
|
||||
runSize("BenchmarkCmdGoSize", filepath.Join(runtime.GOROOT(), "bin/go"))
|
||||
}
|
||||
|
||||
func runHelloSize() {
|
||||
cmd := exec.Command("go", "build", "-o", "_hello_", filepath.Join(runtime.GOROOT(), "test/helloworld.go"))
|
||||
cmd.Stdout = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove("_hello_")
|
||||
runSize("BenchmarkHelloSize", "_hello_")
|
||||
}
|
||||
|
||||
func runSize(name, file string) {
|
||||
info, err := os.Stat(file)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
out, err := exec.Command("size", file).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("size: %v\n%s", err, out)
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
if len(lines) < 2 {
|
||||
log.Printf("not enough output from size: %s", out)
|
||||
return
|
||||
}
|
||||
f := strings.Fields(lines[1])
|
||||
if strings.HasPrefix(lines[0], "__TEXT") && len(f) >= 2 { // OS X
|
||||
fmt.Printf("%s 1 %s text-bytes %s data-bytes %v exe-bytes\n", name, f[0], f[1], info.Size())
|
||||
} else if strings.Contains(lines[0], "bss") && len(f) >= 3 {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
func runBuild(name, dir string) {
|
||||
switch name {
|
||||
case "BenchmarkMakeBash":
|
||||
runMakeBash()
|
||||
return
|
||||
case "BenchmarkCmdGoSize":
|
||||
runCmdGoSize()
|
||||
return
|
||||
case "BenchmarkHelloSize":
|
||||
runHelloSize()
|
||||
return
|
||||
}
|
||||
|
||||
pkg, err := build.Import(dir, ".", 0)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
args := []string{"-o", "_compilebench_.o"}
|
||||
if is6g {
|
||||
*flagMemprofilerate = -1
|
||||
*flagAlloc = false
|
||||
*flagCpuprofile = ""
|
||||
*flagMemprofile = ""
|
||||
}
|
||||
if *flagMemprofilerate >= 0 {
|
||||
args = append(args, "-memprofilerate", fmt.Sprint(*flagMemprofilerate))
|
||||
}
|
||||
args = append(args, strings.Fields(*flagCompilerFlags)...)
|
||||
if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" {
|
||||
if *flagAlloc || *flagMemprofile != "" {
|
||||
args = append(args, "-memprofile", "_compilebench_.memprof")
|
||||
}
|
||||
if *flagCpuprofile != "" {
|
||||
args = append(args, "-cpuprofile", "_compilebench_.cpuprof")
|
||||
}
|
||||
}
|
||||
args = append(args, pkg.GoFiles...)
|
||||
cmd := exec.Command(compiler, args...)
|
||||
cmd.Dir = pkg.Dir
|
||||
cmd.Stdout = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
start := time.Now()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
log.Printf("%v: %v", name, err)
|
||||
return
|
||||
}
|
||||
end := time.Now()
|
||||
|
||||
var allocs, bytes int64
|
||||
if *flagAlloc || *flagMemprofile != "" {
|
||||
out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.memprof")
|
||||
if err != nil {
|
||||
log.Print("cannot find memory profile after compilation")
|
||||
}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 4 || f[0] != "#" || f[2] != "=" {
|
||||
continue
|
||||
}
|
||||
val, err := strconv.ParseInt(f[3], 0, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch f[1] {
|
||||
case "TotalAlloc":
|
||||
bytes = val
|
||||
case "Mallocs":
|
||||
allocs = val
|
||||
}
|
||||
}
|
||||
|
||||
if *flagMemprofile != "" {
|
||||
if err := ioutil.WriteFile(*flagMemprofile, out, 0666); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
os.Remove(pkg.Dir + "/_compilebench_.memprof")
|
||||
}
|
||||
|
||||
if *flagCpuprofile != "" {
|
||||
out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.cpuprof")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(*flagCpuprofile, out, 0666); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
os.Remove(pkg.Dir + "/_compilebench_.cpuprof")
|
||||
}
|
||||
|
||||
wallns := end.Sub(start).Nanoseconds()
|
||||
userns := cmd.ProcessState.UserTime().Nanoseconds()
|
||||
|
||||
if *flagAlloc {
|
||||
fmt.Printf("%s 1 %d ns/op %d user-ns/op %d B/op %d allocs/op\n", name, wallns, userns, bytes, allocs)
|
||||
} else {
|
||||
fmt.Printf("%s 1 %d ns/op %d user-ns/op\n", name, wallns, userns)
|
||||
}
|
||||
|
||||
os.Remove(pkg.Dir + "/_compilebench_.o")
|
||||
}
|
Loading…
Reference in New Issue