├── LICENSE ├── README.md ├── compilecmp ├── error.go └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | compilebench is moving. Please use golang.org/x/tools/cmd/compilebench instead. 2 | -------------------------------------------------------------------------------- /compilecmp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | [ $# = 2 ] || (echo 'usage: compilecmp oldrev newrev' >&2; exit 2) 5 | cd $(go env GOROOT) 6 | 7 | old=$1 8 | case "$old" in 9 | 201[0-9]-*) 10 | old=$(git rev-list -n 1 --before="$old" origin/master) 11 | esac 12 | 13 | new=$2 14 | case "$new" in 15 | 201[0-9]-*) 16 | new=$(git rev-list -n 1 --before="$new" origin/master) 17 | esac 18 | 19 | echo OLD $old: 20 | git log -n1 $old 21 | echo 22 | echo NEW $new: 23 | git log -n1 $new 24 | echo 25 | 26 | rm -rf /tmp/compilecmp.{old,new} 27 | mkdir /tmp/compilecmp.old /tmp/compilecmp.new 28 | git archive --format=tar $old | tar -C /tmp/compilecmp.old -xf - 29 | git archive --format=tar $new | tar -C /tmp/compilecmp.new -xf - 30 | export GOROOT=/XXX 31 | cd /tmp/compilecmp.old/src 32 | echo OLD >../VERSION 33 | ./make.bash 34 | cd /tmp/compilecmp.new/src 35 | echo NEW >../VERSION 36 | ./make.bash 37 | cd /tmp/compilecmp.old 38 | oldcompile=$(GOROOT=/tmp/compilecmp.old go tool -n compile) 39 | GOROOT=/tmp/compilecmp.new GOPATH=/tmp/compilecmp.old/pkg/bootstrap go build -o $oldcompile bootstrap/compile 40 | GOROOT=/tmp/compilecmp.old go install std 41 | 42 | for i in Template Unicode GoTypes 43 | do 44 | echo OLD $old built with $new 45 | GOROOT=/tmp/compilecmp.old compilebench -count=10 -run=$i -compileflags='-bench=/dev/stdout -p='$i 2>&1 | tee -a /tmp/compilecmp.old/times 46 | echo NEW $new built with $new 47 | GOROOT=/tmp/compilecmp.new compilebench -count=10 -run=$i -compileflags='-bench=/dev/stdout -p='$i 2>&1 | tee -a /tmp/compilecmp.new/times 48 | done 49 | echo 50 | echo 51 | echo BENCHSTAT 52 | echo 53 | echo 54 | benchstat /tmp/compilecmp.{old,new}/times 55 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 | package main 6 | 7 | import "os" 8 | 9 | func main() { 10 | os.Stderr.WriteString(`compilebench: stale compilebench 11 | 12 | This program is rsc.io/compilebench. 13 | You should be using golang.org/x/tools/cmd/compilebench. 14 | Suggestion: 15 | 16 | rm -r $GOPATH/src/rsc.io/compilebench 17 | go get -u golang.org/x/tools/cmd/compilebench 18 | 19 | `) 20 | os.Exit(2) 21 | } 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 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 | // +build ignore 6 | 7 | // Compilebench benchmarks the speed of the Go compiler. 8 | // 9 | // Usage: 10 | // 11 | // compilebench [options] 12 | // 13 | // It times the compilation of various packages and prints results in 14 | // the format used by package testing (and expected by rsc.io/benchstat). 15 | // 16 | // The options are: 17 | // 18 | // -alloc 19 | // Report allocations. 20 | // 21 | // -compile exe 22 | // Use exe as the path to the cmd/compile binary. 23 | // 24 | // -compileflags 'list' 25 | // Pass the space-separated list of flags to the compilation. 26 | // 27 | // -count n 28 | // Run each benchmark n times (default 1). 29 | // 30 | // -cpuprofile file 31 | // Write a CPU profile of the compiler to file. 32 | // 33 | // -memprofile file 34 | // Write a memory profile of the compiler to file. 35 | // 36 | // -memprofilerate rate 37 | // Set runtime.MemProfileRate during compilation. 38 | // 39 | // -run regexp 40 | // Only run benchmarks with names matching regexp. 41 | // 42 | // Although -cpuprofile and -memprofile are intended to write a 43 | // combined profile for all the executed benchmarks to file, 44 | // today they write only the profile for the last benchmark executed. 45 | // 46 | // The default memory profiling rate is one profile sample per 512 kB 47 | // allocated (see ``go doc runtime.MemProfileRate''). 48 | // Lowering the rate (for example, -memprofilerate 64000) produces 49 | // a more fine-grained and therefore accurate profile, but it also incurs 50 | // execution cost. For benchmark comparisons, never use timings 51 | // obtained with a low -memprofilerate option. 52 | // 53 | // Example 54 | // 55 | // Assuming the base version of the compiler has been saved with 56 | // ``toolstash save,'' this sequence compares the old and new compiler: 57 | // 58 | // compilebench -count 10 -compile $(toolstash -n compile) >old.txt 59 | // compilebench -count 10 >new.txt 60 | // benchstat old.txt new.txt 61 | // 62 | package main 63 | 64 | import ( 65 | "flag" 66 | "fmt" 67 | "go/build" 68 | "io/ioutil" 69 | "log" 70 | "os" 71 | "os/exec" 72 | "path/filepath" 73 | "regexp" 74 | "runtime" 75 | "strconv" 76 | "strings" 77 | "time" 78 | ) 79 | 80 | var ( 81 | goroot = runtime.GOROOT() 82 | compiler string 83 | runRE *regexp.Regexp 84 | is6g bool 85 | ) 86 | 87 | var ( 88 | flagAlloc = flag.Bool("alloc", false, "report allocations") 89 | flagCompiler = flag.String("compile", "", "use `exe` as the cmd/compile binary") 90 | flagCompilerFlags = flag.String("compileflags", "", "additional `flags` to pass to compile") 91 | flagRun = flag.String("run", "", "run benchmarks matching `regexp`") 92 | flagCount = flag.Int("count", 1, "run benchmarks `n` times") 93 | flagCpuprofile = flag.String("cpuprofile", "", "write CPU profile to `file`") 94 | flagMemprofile = flag.String("memprofile", "", "write memory profile to `file`") 95 | flagMemprofilerate = flag.Int64("memprofilerate", -1, "set memory profile `rate`") 96 | flagShort = flag.Bool("short", false, "skip long-running benchmarks") 97 | ) 98 | 99 | var tests = []struct { 100 | name string 101 | dir string 102 | long bool 103 | }{ 104 | {"BenchmarkTemplate", "html/template", false}, 105 | {"BenchmarkUnicode", "unicode", false}, 106 | {"BenchmarkGoTypes", "go/types", false}, 107 | {"BenchmarkCompiler", "cmd/compile/internal/gc", false}, 108 | {"BenchmarkMakeBash", "", true}, 109 | {"BenchmarkHelloSize", "", false}, 110 | {"BenchmarkCmdGoSize", "", true}, 111 | } 112 | 113 | func usage() { 114 | fmt.Fprintf(os.Stderr, "usage: compilebench [options]\n") 115 | fmt.Fprintf(os.Stderr, "options:\n") 116 | flag.PrintDefaults() 117 | os.Exit(2) 118 | } 119 | 120 | func main() { 121 | log.SetFlags(0) 122 | log.SetPrefix("compilebench: ") 123 | flag.Usage = usage 124 | flag.Parse() 125 | if flag.NArg() != 0 { 126 | usage() 127 | } 128 | 129 | compiler = *flagCompiler 130 | if compiler == "" { 131 | out, err := exec.Command("go", "tool", "-n", "compile").CombinedOutput() 132 | if err != nil { 133 | out, err = exec.Command("go", "tool", "-n", "6g").CombinedOutput() 134 | is6g = true 135 | if err != nil { 136 | out, err = exec.Command("go", "tool", "-n", "compile").CombinedOutput() 137 | log.Fatalf("go tool -n compiler: %v\n%s", err, out) 138 | } 139 | } 140 | compiler = strings.TrimSpace(string(out)) 141 | } 142 | 143 | if *flagRun != "" { 144 | r, err := regexp.Compile(*flagRun) 145 | if err != nil { 146 | log.Fatalf("invalid -run argument: %v", err) 147 | } 148 | runRE = r 149 | } 150 | 151 | for i := 0; i < *flagCount; i++ { 152 | for _, tt := range tests { 153 | if tt.long && *flagShort { 154 | continue 155 | } 156 | if runRE == nil || runRE.MatchString(tt.name) { 157 | runBuild(tt.name, tt.dir) 158 | } 159 | } 160 | } 161 | } 162 | 163 | func runCmd(name string, cmd *exec.Cmd) { 164 | start := time.Now() 165 | out, err := cmd.CombinedOutput() 166 | if err != nil { 167 | log.Printf("%v: %v\n%s", name, err, out) 168 | return 169 | } 170 | fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds()) 171 | } 172 | 173 | func runMakeBash() { 174 | cmd := exec.Command("./make.bash") 175 | cmd.Dir = filepath.Join(runtime.GOROOT(), "src") 176 | runCmd("BenchmarkMakeBash", cmd) 177 | } 178 | 179 | func runCmdGoSize() { 180 | runSize("BenchmarkCmdGoSize", filepath.Join(runtime.GOROOT(), "bin/go")) 181 | } 182 | 183 | func runHelloSize() { 184 | cmd := exec.Command("go", "build", "-o", "_hello_", filepath.Join(runtime.GOROOT(), "test/helloworld.go")) 185 | cmd.Stdout = os.Stderr 186 | cmd.Stderr = os.Stderr 187 | if err := cmd.Run(); err != nil { 188 | log.Print(err) 189 | return 190 | } 191 | defer os.Remove("_hello_") 192 | runSize("BenchmarkHelloSize", "_hello_") 193 | } 194 | 195 | func runSize(name, file string) { 196 | info, err := os.Stat(file) 197 | if err != nil { 198 | log.Print(err) 199 | return 200 | } 201 | out, err := exec.Command("size", file).CombinedOutput() 202 | if err != nil { 203 | log.Printf("size: %v\n%s", err, out) 204 | return 205 | } 206 | lines := strings.Split(string(out), "\n") 207 | if len(lines) < 2 { 208 | log.Printf("not enough output from size: %s", out) 209 | return 210 | } 211 | f := strings.Fields(lines[1]) 212 | if strings.HasPrefix(lines[0], "__TEXT") && len(f) >= 2 { // OS X 213 | fmt.Printf("%s 1 %s text-bytes %s data-bytes %v exe-bytes\n", name, f[0], f[1], info.Size()) 214 | } else if strings.Contains(lines[0], "bss") && len(f) >= 3 { 215 | 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()) 216 | } 217 | } 218 | 219 | func runBuild(name, dir string) { 220 | switch name { 221 | case "BenchmarkMakeBash": 222 | runMakeBash() 223 | return 224 | case "BenchmarkCmdGoSize": 225 | runCmdGoSize() 226 | return 227 | case "BenchmarkHelloSize": 228 | runHelloSize() 229 | return 230 | } 231 | 232 | pkg, err := build.Import(dir, ".", 0) 233 | if err != nil { 234 | log.Print(err) 235 | return 236 | } 237 | args := []string{"-o", "_compilebench_.o"} 238 | if is6g { 239 | *flagMemprofilerate = -1 240 | *flagAlloc = false 241 | *flagCpuprofile = "" 242 | *flagMemprofile = "" 243 | } 244 | if *flagMemprofilerate >= 0 { 245 | args = append(args, "-memprofilerate", fmt.Sprint(*flagMemprofilerate)) 246 | } 247 | args = append(args, strings.Fields(*flagCompilerFlags)...) 248 | if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" { 249 | if *flagAlloc || *flagMemprofile != "" { 250 | args = append(args, "-memprofile", "_compilebench_.memprof") 251 | } 252 | if *flagCpuprofile != "" { 253 | args = append(args, "-cpuprofile", "_compilebench_.cpuprof") 254 | } 255 | } 256 | args = append(args, pkg.GoFiles...) 257 | cmd := exec.Command(compiler, args...) 258 | cmd.Dir = pkg.Dir 259 | cmd.Stdout = os.Stderr 260 | cmd.Stderr = os.Stderr 261 | start := time.Now() 262 | err = cmd.Run() 263 | if err != nil { 264 | log.Printf("%v: %v", name, err) 265 | return 266 | } 267 | end := time.Now() 268 | 269 | var allocs, bytes int64 270 | if *flagAlloc || *flagMemprofile != "" { 271 | out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.memprof") 272 | if err != nil { 273 | log.Print("cannot find memory profile after compilation") 274 | } 275 | for _, line := range strings.Split(string(out), "\n") { 276 | f := strings.Fields(line) 277 | if len(f) < 4 || f[0] != "#" || f[2] != "=" { 278 | continue 279 | } 280 | val, err := strconv.ParseInt(f[3], 0, 64) 281 | if err != nil { 282 | continue 283 | } 284 | switch f[1] { 285 | case "TotalAlloc": 286 | bytes = val 287 | case "Mallocs": 288 | allocs = val 289 | } 290 | } 291 | 292 | if *flagMemprofile != "" { 293 | if err := ioutil.WriteFile(*flagMemprofile, out, 0666); err != nil { 294 | log.Print(err) 295 | } 296 | } 297 | os.Remove(pkg.Dir + "/_compilebench_.memprof") 298 | } 299 | 300 | if *flagCpuprofile != "" { 301 | out, err := ioutil.ReadFile(pkg.Dir + "/_compilebench_.cpuprof") 302 | if err != nil { 303 | log.Print(err) 304 | } 305 | if err := ioutil.WriteFile(*flagCpuprofile, out, 0666); err != nil { 306 | log.Print(err) 307 | } 308 | os.Remove(pkg.Dir + "/_compilebench_.cpuprof") 309 | } 310 | 311 | wallns := end.Sub(start).Nanoseconds() 312 | userns := cmd.ProcessState.UserTime().Nanoseconds() 313 | 314 | if *flagAlloc { 315 | fmt.Printf("%s 1 %d ns/op %d user-ns/op %d B/op %d allocs/op\n", name, wallns, userns, bytes, allocs) 316 | } else { 317 | fmt.Printf("%s 1 %d ns/op %d user-ns/op\n", name, wallns, userns) 318 | } 319 | 320 | os.Remove(pkg.Dir + "/_compilebench_.o") 321 | } 322 | --------------------------------------------------------------------------------