Skip to content

cmd/go: go run pkg is significantly slower than running built binary #25416

Closed
@myitcv

Description

@myitcv

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version devel +212c9479e3 Tue May 15 16:29:04 2018 +0000 linux/amd64

Does this issue reproduce with the latest release?

n/a: this relies on changes in tip.

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOCACHE="/home/myitcv/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/myitcv/gostuff"
GORACE=""
GOROOT="/home/myitcv/gos"
GOTMPDIR=""
GOTOOLDIR="/home/myitcv/gos/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build404056765=/tmp/go-build -gno-record-gcc-switches"

What did you do?

cd `mktemp -d`
export GOPATH=$PWD
mkdir -p src/example.com
cat <<EOD > src/example.com/main.go
package main

import (
        "fmt"
)

func main() {
        fmt.Println("Hello, world!")
}
EOD

cat <<EOD > src/example.com/main_test.go
package main_test

import (
        "fmt"
        "io/ioutil"
        "os"
        "os/exec"
        "strings"
        "testing"
)

const (
        testPkg = "example.com"
)

func BenchmarkGoRun(b *testing.B) {
        for n := 0; n < b.N; n++ {
                cmd := exec.Command("go", "run", testPkg)
                out, err := cmd.CombinedOutput()
                if err != nil {
                        panic(fmt.Errorf("failed to %v: %v\n%s", strings.Join(cmd.Args, " "), err, out))
                }
        }
}

func BenchmarkGoBuild(b *testing.B) {
        tf, err := ioutil.TempFile("", "")
        if err != nil {
                b.Fatalf("failed to create temp file: %v", err)
        }
        defer os.Remove(tf.Name())

        {
                cmd := exec.Command("go", "build", "-o", tf.Name(), testPkg)
                out, err := cmd.CombinedOutput()
                if err != nil {
                        b.Fatalf("%v failed: %v\n%s", strings.Join(cmd.Args, " "), err, out)
                }
        }

        b.ResetTimer()

        for n := 0; n < b.N; n++ {
                cmd := exec.Command(tf.Name())
                out, err := cmd.CombinedOutput()
                if err != nil {
                        b.Fatalf("%v failed: %v\n%s", strings.Join(cmd.Args, " "), err, out)
                }
        }
}
EOD

go test -test.bench . example.com

What did you expect to see?

The benchmark with go run example.com to be more comparable with the benchmark that runs a binary directly.

What did you see instead?

The go run example.com benchmark is ~180 times slower.

goos: linux
goarch: amd64
pkg: example.com
BenchmarkGoRun-8              10         163497281 ns/op
BenchmarkGoBuild-8          2000            893443 ns/op
PASS
ok      example.com     4.260s

I recall @rsc mentioning somewhere (can't recall where) that the result of go run pkg is not cached, which I think accounts for the difference above.

One of the major benefits of the go run pkg form is that it is possible to unambiguously identify the program in question. My particular use case is //go:generate directives, where it then becomes possible to calculate the go generate dependency graph.

Ideally I would like to replace all of my //go:generate abc directives with //go:generate example.com/p/abc, but the difference in speeds observed above makes this infeasible.

So I'm raising this as an issue to discuss whether it would be worth caching the output of go run pkg. I can't claim to understand any of the pros/cons here so would appreciate thoughts.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions