cmd/bent: change to use module-mode for building
also tweaks for performance dashboard
also build tags to turn off broken unsafe code in some benchmarks
also corrected profiling for new run directory
Change-Id: I27f484e52de4ce55f90bb3f122be72127a3181b4
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/354410
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
diff --git a/cmd/bent/bent.go b/cmd/bent/bent.go
index 5eaa269..4081800 100644
--- a/cmd/bent/bent.go
+++ b/cmd/bent/bent.go
@@ -20,7 +20,6 @@
"os"
"os/exec"
"path"
- "path/filepath"
"runtime"
"strconv"
"strings"
@@ -44,8 +43,9 @@
BuildFlags []string // Flags for building test (e.g., -tags purego)
RunWrapper []string // (Inner) Command and args to precede whatever the operation is; may fail in the sandbox.
// e.g. benchmark may run as ConfigWrapper ConfigArg BenchWrapper BenchArg ActualBenchmark
- NotSandboxed bool // True if this benchmark cannot or should not be run in a container.
- Disabled bool // True if this benchmark is temporarily disabled.
+ NotSandboxed bool // True if this benchmark cannot or should not be run in a container.
+ Disabled bool // True if this benchmark is temporarily disabled.
+ RunDir string // Parent directory of testdata.
}
type Todo struct {
@@ -110,26 +110,10 @@
var runstamp = strings.Replace(strings.Replace(time.Now().Format("2006-01-02T15:04:05"), "-", "", -1), ":", "", -1)
func cleanup(gopath string) {
- pkg, bin := path.Join(gopath, "pkg"), path.Join(gopath, "bin")
+ bin := path.Join(gopath, "bin")
if verbose > 0 {
- fmt.Printf("chmod -R u+w %s\n", pkg)
+ fmt.Printf("rm -rf %s\n", bin)
}
- // Necessary to make directories writeable with new module stuff.
- filepath.Walk(pkg, func(path string, info os.FileInfo, err error) error {
- if path != "" && info != nil {
- if mode := info.Mode(); 0 == mode&os.ModeSymlink {
- err := os.Chmod(path, 0200|mode)
- if err != nil {
- panic(err)
- }
- }
- }
- return nil
- })
- if verbose > 0 {
- fmt.Printf("rm -rf %s %s\n", pkg, bin)
- }
- os.RemoveAll(pkg)
os.RemoveAll(bin)
}
@@ -203,36 +187,44 @@
flag.Parse()
- // Make sure our filesystem is in good shape.
- if err := checkAndSetUpFileSystem(initialize); err != nil {
- fmt.Printf("%v", err)
- os.Exit(1)
- }
-
// Fail early if either of these commands is missing.
_, errTime := exec.LookPath("/usr/bin/time")
_, errRsync := exec.LookPath("rsync")
if errTime != nil && errRsync != nil {
- println("This program needs /usr/bin/time and rsync commands to run")
+ fmt.Println("This program needs /usr/bin/time and rsync commands to run")
os.Exit(1)
}
if errRsync != nil {
- println("This program needs the rsync command to run")
+ fmt.Println("This program needs the rsync command to run")
os.Exit(1)
}
if errTime != nil {
- println("This program needs the /usr/bin/time command to run")
+ fmt.Println("This program needs the /usr/bin/time command to run")
os.Exit(1)
}
if requireSandbox {
_, errDocker := exec.LookPath("docker")
if errDocker != nil {
- println("Sandboxing benchmarks requires the docker command")
+ fmt.Println("Sandboxing benchmarks requires the docker command")
os.Exit(1)
}
}
+ // Make sure our filesystem is in good shape.
+ if err := checkAndSetUpFileSystem(initialize); err != nil {
+ fmt.Printf("%v", err)
+ os.Exit(1)
+ }
+
+ var err error
+ // Create any directories we need.
+ dirs, err = createDirectories(0775)
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
todo := &Todo{}
blobB, err := ioutil.ReadFile(benchFile)
if err != nil {
@@ -251,16 +243,6 @@
os.Exit(1)
}
- // Create any directories we need.
- dirs, err := createDirectories(0775)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- for i := range todo.Configurations {
- todo.Configurations[i].dirs = dirs
- }
-
var moreArgs []string
if flag.NArg() > 0 {
for i, arg := range flag.Args() {
@@ -488,6 +470,26 @@
fmt.Print("Go getting")
}
+ goDotMod := path.Join(dirs.build, "go.mod")
+
+ if _, err := os.Stat(goDotMod); err != nil { // if error assume go.mod does not exist
+ cmd := exec.Command("go", "mod", "init", "build")
+ cmd.Env = defaultEnv
+ cmd.Dir = dirs.build
+
+ if verbose > 0 {
+ fmt.Println(asCommandLine(dirs.wd, cmd))
+ } else {
+ fmt.Print(".")
+ }
+ _, err := cmd.Output()
+ if err != nil {
+ ee := err.(*exec.ExitError)
+ fmt.Printf("There was an error running 'go mod init', stderr = %s", ee.Stderr)
+ os.Exit(2)
+ }
+ }
+
// Obtain (go get -d -t -v bench.Repo) all benchmarks, once, populating src
for i, bench := range todo.Benchmarks {
if bench.Disabled {
@@ -497,6 +499,7 @@
cmd := exec.Command("go", "get", "-d", "-t", "-v", bench.Repo)
cmd.Env = getBuildEnv
+ cmd.Dir = dirs.build
if !bench.NotSandboxed { // Do this so that OS-dependent dependencies are done correctly.
cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
@@ -516,54 +519,6 @@
continue
}
- // Ensure testdir exists -- if modules are enabled, it does not.
- // This involves invoking git to make it appear.
- testdir := path.Join(dirs.gopath, "src", bench.Repo)
- _, terr := os.Stat(testdir)
- if terr != nil { // Assume missing directory is the cause of the error.
- parts := strings.Split(bench.Repo, "/")
- root := parts[0]
- repoAt := pathLengths[root] - 1
- if repoAt < 1 || repoAt >= len(parts) {
- s := fmt.Sprintf("repoAt=%d was not a valid index for %v", repoAt, parts)
- fmt.Println(s + "DISABLING benchmark " + bench.Name)
- getAndBuildFailures = append(getAndBuildFailures, s+"("+bench.Name+")\n")
- todo.Benchmarks[i].Disabled = true
- continue
- }
- dirToMake := path.Join(dirs.gopath, "src", path.Join(parts[:repoAt]...))
- repoToGet := path.Join(parts[:repoAt+1]...)
- if verbose > 0 {
- fmt.Printf("mkdir -p %s\n", dirToMake)
- }
- err := os.MkdirAll(dirToMake, 0777)
- if err != nil {
- s := fmt.Sprintf("could not os.MkdirAll(%s), err = %v", dirToMake, err)
- fmt.Println(s + "DISABLING benchmark " + bench.Name)
- getAndBuildFailures = append(getAndBuildFailures, s+"("+bench.Name+")\n")
- todo.Benchmarks[i].Disabled = true
- continue
- }
-
- cmd = exec.Command("git", "clone", "https://"+repoToGet)
- cmd.Env = defaultEnv
- cmd.Dir = dirToMake
- if verbose > 0 {
- fmt.Println(asCommandLine(dirs.wd, cmd))
- } else {
- fmt.Print(".")
- }
- _, err = cmd.Output()
- if err != nil {
- ee := err.(*exec.ExitError)
- s := fmt.Sprintf("There was an error running 'git clone', stderr = %s", ee.Stderr)
- fmt.Println(s + "DISABLING benchmark " + bench.Name)
- getAndBuildFailures = append(getAndBuildFailures, s+"("+bench.Name+")\n")
- todo.Benchmarks[i].Disabled = true
- continue
- }
- }
-
needSandbox = !bench.NotSandboxed || needSandbox
needNotSandbox = bench.NotSandboxed || needNotSandbox
}
@@ -807,6 +762,41 @@
}
}
+ // Initialize RunDir for benchmarks.
+ for i, bench := range todo.Benchmarks {
+ if bench.Disabled {
+ continue
+ }
+ // Obtain directory containing testdata, if any:
+ // Capture output of "go list -f {{.Dir}} $PKG"
+
+ cmd := exec.Command("go", "list", "-f", "{{.Dir}}", bench.Repo)
+ cmd.Env = defaultEnv
+ cmd.Dir = dirs.build
+
+ if verbose > 0 {
+ fmt.Println(asCommandLine(dirs.wd, cmd))
+ } else {
+ fmt.Print(".")
+ }
+ out, err := cmd.Output()
+ if err != nil {
+ s := fmt.Sprintf(`could not go list -f {{.Dir}} %s, err=%v`, bench.Repo, err)
+ fmt.Println(s + "DISABLING benchmark " + bench.Name)
+ getAndBuildFailures = append(getAndBuildFailures, s+"("+bench.Name+")\n")
+ todo.Benchmarks[i].Disabled = true
+ continue
+ } else if verbose > 0 {
+ fmt.Printf("# Rundir=%s\n", string(out))
+ }
+ rundir := strings.TrimSpace(string(out))
+ if !bench.NotSandboxed {
+ // if sandboxed, strip cwd from prefix of rundir.
+ rundir = rundir[len(dirs.wd):]
+ }
+ todo.Benchmarks[i].RunDir = rundir
+ }
+
var failures []string
// If there's a bad error running one of the benchmarks, report what we've got, please.
@@ -887,7 +877,6 @@
}
if b.NotSandboxed {
- testdir := path.Join(dirs.gopath, "src", b.Repo)
bin := path.Join(dirs.wd, dirs.testBinDir, testBinaryName)
wrappersAndBin = append(wrappersAndBin, bin)
@@ -895,31 +884,33 @@
cmd.Args = append(cmd.Args, "-test.run="+b.Tests)
cmd.Args = append(cmd.Args, "-test.bench="+b.Benchmarks)
- cmd.Dir = testdir
+ cmd.Dir = b.RunDir
cmd.Env = defaultEnv
if root != "" {
cmd.Env = replaceEnv(cmd.Env, "GOROOT", root)
}
cmd.Env = replaceEnvs(cmd.Env, config.RunEnv)
cmd.Env = append(cmd.Env, "BENT_DIR="+dirs.wd)
+ cmd.Env = append(cmd.Env, "BENT_PROFILES="+path.Join(dirs.wd, config.thingBenchName("profiles")))
cmd.Env = append(cmd.Env, "BENT_BINARY="+testBinaryName)
cmd.Env = append(cmd.Env, "BENT_I="+strconv.FormatInt(int64(i), 10))
cmd.Args = append(cmd.Args, config.RunFlags...)
cmd.Args = append(cmd.Args, moreArgs...)
+
+ config.say("shortname: " + b.Name + "\n")
s, rc = todo.Configurations[j].runBinary(dirs.wd, cmd, false)
} else {
// docker run --net=none -e GOROOT=... -w /src/github.com/minio/minio/cmd $D /testbin/cmd_Config.test -test.short -test.run=Nope -test.v -test.bench=Benchmark'(Get|Put|List)'
// TODO(jfaller): I don't think we need either of these "/" below, investigate...
- testdir := "/" + path.Join("gopath", "src", b.Repo)
bin := "/" + path.Join(dirs.testBinDir, testBinaryName)
wrappersAndBin = append(wrappersAndBin, bin)
- cmd := exec.Command("docker", "run", "--net=none",
- "-w", testdir)
+ cmd := exec.Command("docker", "run", "--net=none", "-w", b.RunDir)
for _, e := range config.RunEnv {
cmd.Args = append(cmd.Args, "-e", e)
}
cmd.Args = append(cmd.Args, "-e", "BENT_DIR=/") // TODO this is not going to work well
+ cmd.Args = append(cmd.Args, "-e", "BENT_PROFILES="+path.Join(dirs.wd, config.thingBenchName("profiles")))
cmd.Args = append(cmd.Args, "-e", "BENT_BINARY="+testBinaryName)
cmd.Args = append(cmd.Args, "-e", "BENT_I="+strconv.FormatInt(int64(i), 10))
cmd.Args = append(cmd.Args, container)
@@ -928,6 +919,7 @@
cmd.Args = append(cmd.Args, "-test.bench="+b.Benchmarks)
cmd.Args = append(cmd.Args, config.RunFlags...)
cmd.Args = append(cmd.Args, moreArgs...)
+ config.say("shortname: " + b.Name + "\n")
s, rc = todo.Configurations[j].runBinary(dirs.wd, cmd, false)
}
if s != "" {
@@ -990,16 +982,7 @@
// To avoid bad surprises, look for pkg and bin, if they exist, refuse to run
_, derr := os.Stat("Dockerfile")
_, perr := os.Stat(path.Join("gopath", "pkg"))
- _, berr := os.Stat(path.Join("gopath", "bin"))
- _, serr := os.Stat(path.Join("gopath", "src")) // existence of src prevents initialization of Dockerfile
- if perr == nil || berr == nil {
- if !force {
- return errors.New("Building/running tests will trash gopath/pkg and gopath/bin, please remove, rename or run in another directory, or use -f to force.\n")
- }
- fmt.Printf("Building/running tests will trash gopath/pkg and gopath/bin, but force, so removing.\n")
- cleanup("gopath")
- }
if derr != nil && !shouldInit {
// Missing Dockerfile
return errors.New("Missing 'Dockerfile', please rerun with -I (initialize) flag if you intend to use this directory.\n")
@@ -1011,8 +994,8 @@
// Initialize the directory, copying in default benchmarks and sample configurations, and creating a Dockerfile
if shouldInit {
- if serr == nil {
- fmt.Printf("It looks like you've already initialized this directory, remove ./gopath if you want to reinit.\n")
+ if perr == nil {
+ fmt.Printf("It looks like you've already initialized this directory, remove ./gopath/pkg if you want to reinit.\n")
os.Exit(1)
}
for _, s := range copyExes {
@@ -1062,7 +1045,7 @@
}
type directories struct {
- wd, gopath, goroots, testBinDir, benchDir string
+ wd, gopath, goroots, build, testBinDir, benchDir string
}
// createDirectories creates all the directories we need.
@@ -1076,10 +1059,11 @@
wd: cwd,
gopath: path.Join(cwd, "gopath"),
goroots: path.Join(cwd, "goroots"),
+ build: path.Join(cwd, "build"),
testBinDir: "testbin",
benchDir: "bench",
}
- for _, d := range []string{dirs.gopath, dirs.goroots, path.Join(cwd, dirs.testBinDir), path.Join(cwd, dirs.benchDir)} {
+ for _, d := range []string{dirs.gopath, dirs.goroots, dirs.build, path.Join(cwd, dirs.testBinDir), path.Join(cwd, dirs.benchDir)} {
if err := os.Mkdir(d, mode); err != nil && !errors.Is(err, fs.ErrExist) {
return nil, fmt.Errorf("error creating %v: %v", d, err)
}
diff --git a/cmd/bent/configs/benchmarks-50.toml b/cmd/bent/configs/benchmarks-50.toml
index 2ce36be..6b79324 100644
--- a/cmd/bent/configs/benchmarks-50.toml
+++ b/cmd/bent/configs/benchmarks-50.toml
@@ -114,16 +114,19 @@
[[Benchmarks]]
Name = "gonum_blas_native"
Repo = "gonum.org/v1/gonum/blas/gonum"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "Benchmark(DasumMediumUnitaryInc|Dnrm2MediumPosInc)" # not all benchmarks
[[Benchmarks]]
Name = "gonum_lapack_native"
Repo = "gonum.org/v1/gonum/lapack/gonum"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "BenchmarkDgeev/Circulant10"
[[Benchmarks]]
Name = "gonum_mat"
Repo = "gonum.org/v1/gonum/mat"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "Benchmark(MulWorkspaceDense1000Hundredth|ScaleVec10000Inc20)"
[[Benchmarks]]
@@ -156,21 +159,25 @@
[[Benchmarks]]
Name = "gonum_topo"
Repo = "gonum.org/v1/gonum/graph/topo/"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "Benchmark(TarjanSCCGnp_1000_half|TarjanSCCGnp_10_tenth)"
[[Benchmarks]]
Name = "gonum_path"
Repo = "gonum.org/v1/gonum/graph/path/"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "Benchmark(AStarUndirectedmallWorld_10_2_2_2_Heur|Dominators/nested_if_n256)"
[[Benchmarks]]
Name = "gonum_community"
Repo = "gonum.org/v1/gonum/graph/community/"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "BenchmarkLouvainDirectedMultiplex"
[[Benchmarks]]
Name = "gonum_traverse"
Repo = "gonum.org/v1/gonum/graph/traverse/"
+ BuildFlags = ["-tags", "safe"]
Benchmarks = "BenchmarkWalkAllBreadthFirstGnp_(10|1000)_tenth" # more difference by size than anything else
[[Benchmarks]]
diff --git a/cmd/bent/configuration.go b/cmd/bent/configuration.go
index a8fdf1a..89f1f25 100644
--- a/cmd/bent/configuration.go
+++ b/cmd/bent/configuration.go
@@ -37,10 +37,11 @@
Disabled bool // True if this configuration is temporarily disabled
buildStats []BenchStat
benchWriter *os.File
- rootCopy string // The contents of GOROOT are copied here to allow benchmarking of just the test compilation.
- dirs *directories // Test configuration
+ rootCopy string // The contents of GOROOT are copied here to allow benchmarking of just the test compilation.
}
+var dirs *directories // constant across all configurations, useful in other contexts.
+
func (c *Configuration) buildBenchName() string {
return c.thingBenchName("build")
}
@@ -49,7 +50,7 @@
if len(suffix) != 0 {
suffix = path.Base(suffix)
}
- return path.Join(c.dirs.benchDir, runstamp+"."+c.Name+"."+suffix)
+ return path.Join(dirs.benchDir, runstamp+"."+c.Name+"."+suffix)
}
func (c *Configuration) benchName(b *Benchmark) string {
@@ -119,7 +120,7 @@
continue
}
testBinaryName := config.benchName(b)
- c := exec.Command(cmd, path.Join(cwd, config.dirs.testBinDir, testBinaryName), b.Name)
+ c := exec.Command(cmd, path.Join(cwd, dirs.testBinDir, testBinaryName), b.Name)
c.Env = defaultEnv
if !b.NotSandboxed {
@@ -173,6 +174,8 @@
// Prefix with time for build benchmarking:
cmd := exec.Command("/usr/bin/time", "-p", gocmd, "test", "-vet=off", "-c")
+ compileTo := path.Join(dirs.wd, dirs.testBinDir, config.benchName(bench))
+ cmd.Args = append(cmd.Args, "-o", compileTo)
cmd.Args = append(cmd.Args, bench.BuildFlags...)
// Do not normally need -a because cache was emptied first and std was -a installed with these flags.
// But for -a=1, do it anyway
@@ -183,8 +186,8 @@
if config.GcFlags != "" {
cmd.Args = append(cmd.Args, "-gcflags="+config.GcFlags)
}
- cmd.Args = append(cmd.Args, ".")
- cmd.Dir = path.Join(gopath, "src", bench.Repo)
+ cmd.Args = append(cmd.Args, bench.Repo)
+ cmd.Dir = dirs.build // use module-mode
cmd.Env = defaultEnv
if !bench.NotSandboxed {
cmd.Env = replaceEnv(cmd.Env, "GOOS", "linux")
@@ -251,18 +254,8 @@
f.Sync()
f.Close()
- // Move generated binary to well-known place.
- from := path.Join(cmd.Dir, bench.testBinaryName())
- to := path.Join(config.dirs.wd, config.dirs.testBinDir, config.benchName(bench))
- err = os.Rename(from, to)
- if err != nil {
- fmt.Printf("There was an error renaming %s to %s, %v\n", from, to, err)
- cleanup(gopath)
- os.Exit(1)
- }
// Trim /usr/bin/time info from soutput, it's ugly
if verbose > 0 {
- fmt.Println("mv " + from + " " + to + "")
i := strings.LastIndex(soutput, "real")
if i >= 0 {
soutput = soutput[:i]
@@ -278,6 +271,17 @@
return ""
}
+// say writes s to c's benchmark output file
+func (c *Configuration) say(s string) {
+ b := []byte(s)
+ nw, err := c.benchWriter.Write(b)
+ if err != nil {
+ fmt.Printf("Error writing, err = %v, nwritten = %d, nrequested = %d\n", err, nw, len(b))
+ }
+ c.benchWriter.Sync()
+ fmt.Print(string(b))
+}
+
// runBinary runs cmd and displays the output.
// If the command returns an error, returns an error string.
func (c *Configuration) runBinary(cwd string, cmd *exec.Cmd, printWorkingDot bool) (string, int) {
diff --git a/cmd/bent/scripts/cpuprofile b/cmd/bent/scripts/cpuprofile
index c6b3d0a..111653f 100755
--- a/cmd/bent/scripts/cpuprofile
+++ b/cmd/bent/scripts/cpuprofile
@@ -1,8 +1,9 @@
#!/bin/bash
# Run args as command, but run cpuprofile and then pprof to capture test cpuprofile output
-pf="${BENT_BINARY}_${BENT_I}.prof"
+pf="${BENT_PROFILES}/${BENT_BINARY}_${BENT_I}.prof"
+mkdir -p ${BENT_PROFILES}
"$@" -test.cpuprofile="$pf"
-echo cpuprofile in `pwd`/"$pf"
+echo cpuprofile in "$pf"
if [[ x`which pprof` == x"" ]] ; then
go tool pprof -text -flat -nodecount=20 "$pf"
else
diff --git a/cmd/bent/scripts/memprofile b/cmd/bent/scripts/memprofile
index a8207a1..b459da5 100755
--- a/cmd/bent/scripts/memprofile
+++ b/cmd/bent/scripts/memprofile
@@ -1,6 +1,8 @@
#!/bin/bash
# Run args as command, but run memprofile and then pprof to capture test memprofile output
-mpf="${BENT_BINARY}_${BENT_I}.mprof"
+mpf="${BENT_PROFILES}/${BENT_BINARY}_${BENT_I}.mprof"
+mkdir -p ${BENT_PROFILES}
+
"$@" -test.memprofile="$mpf"
-echo memprofile in `pwd`/"$mpf"
+echo memprofile in "$mpf"
go tool pprof --alloc_space --text --cum --nodecount=20 "$mpf"