Skip to content

Commit f4e5bd4

Browse files
committed
cmd/go: add support for concurrent backend compilation
It is disabled by default. It can be enabled by setting the environment variable GO19CONCURRENTCOMPILATION=1. Benchmarking results are presented in a grid. Columns are different values of c (compiler backend concurrency); rows are different values of p (process concurrency). 'go build -a std cmd', a 4 core raspberry pi 3: c=1 c=2 c=4 StdCmd/p=1 504s ± 2% 413s ± 4% 367s ± 3% StdCmd/p=2 314s ± 3% 266s ± 4% 267s ± 4% StdCmd/p=4 254s ± 5% 241s ± 5% 238s ± 6% 'go build -a std cmd', an 8 core darwin/amd64 laptop: c=1 c=2 c=4 c=6 c=8 StdCmd/p=1 40.4s ± 7% 31.0s ± 1% 27.3s ± 1% 27.8s ± 0% 27.7s ± 0% StdCmd/p=2 21.9s ± 1% 17.9s ± 1% 16.9s ± 1% 17.0s ± 1% 17.2s ± 0% StdCmd/p=4 17.4s ± 2% 14.5s ± 2% 13.3s ± 2% 13.5s ± 2% 13.6s ± 2% StdCmd/p=6 16.9s ± 1% 14.2s ± 2% 13.1s ± 2% 13.2s ± 2% 13.3s ± 2% StdCmd/p=8 16.7s ± 2% 14.2s ± 2% 13.2s ± 3% 13.2s ± 2% 13.4s ± 2% 'go build -a std cmd', a 96 core arm64 server: c=1 c=2 c=4 c=6 c=8 c=16 c=32 c=64 c=96 StdCmd/p=1 173s ± 1% 133s ± 1% 114s ± 1% 109s ± 1% 106s ± 0% 106s ± 1% 107s ± 1% 110s ± 1% 113s ± 1% StdCmd/p=2 94.2s ± 2% 71.5s ± 1% 61.7s ± 1% 58.7s ± 1% 57.5s ± 2% 56.9s ± 1% 58.0s ± 1% 59.6s ± 1% 61.0s ± 1% StdCmd/p=4 74.1s ± 2% 53.5s ± 1% 43.7s ± 2% 40.5s ± 1% 39.2s ± 2% 38.9s ± 2% 39.5s ± 3% 40.3s ± 2% 40.8s ± 1% StdCmd/p=6 69.3s ± 1% 50.2s ± 2% 40.3s ± 2% 37.3s ± 3% 36.0s ± 3% 35.3s ± 2% 36.0s ± 2% 36.8s ± 2% 37.5s ± 2% StdCmd/p=8 66.1s ± 2% 47.7s ± 2% 38.6s ± 2% 35.7s ± 2% 34.4s ± 1% 33.6s ± 2% 34.2s ± 2% 34.6s ± 1% 35.0s ± 1% StdCmd/p=16 63.4s ± 2% 45.3s ± 2% 36.3s ± 2% 33.3s ± 2% 32.0s ± 3% 31.6s ± 2% 32.1s ± 2% 32.5s ± 2% 32.7s ± 2% StdCmd/p=32 62.2s ± 1% 44.2s ± 2% 35.3s ± 2% 32.4s ± 2% 31.2s ± 2% 30.9s ± 2% 31.1s ± 2% 31.7s ± 2% 32.0s ± 2% StdCmd/p=64 62.2s ± 1% 44.3s ± 2% 35.4s ± 2% 32.4s ± 2% 31.2s ± 2% 30.9s ± 2% 31.2s ± 2% 31.8s ± 3% 32.2s ± 3% StdCmd/p=96 62.2s ± 2% 44.4s ± 2% 35.3s ± 2% 32.3s ± 2% 31.1s ± 2% 30.9s ± 3% 31.3s ± 2% 31.7s ± 1% 32.1s ± 2% benchjuju, an 8 core darwin/amd64 laptop: c=1 c=2 c=4 c=6 c=8 BuildJuju/p=1 55.3s ± 0% 46.3s ± 0% 41.9s ± 0% 41.4s ± 1% 41.3s ± 0% BuildJuju/p=2 33.7s ± 1% 28.4s ± 1% 26.7s ± 1% 26.6s ± 1% 26.8s ± 1% BuildJuju/p=4 24.7s ± 1% 22.3s ± 1% 21.4s ± 1% 21.7s ± 1% 21.8s ± 1% BuildJuju/p=6 20.6s ± 1% 19.3s ± 2% 19.4s ± 1% 19.7s ± 1% 19.9s ± 1% BuildJuju/p=8 20.6s ± 2% 19.5s ± 2% 19.3s ± 2% 19.6s ± 1% 19.8s ± 2% Updates #15756 Change-Id: I8a56e88953071a05eee764002024c54cd888a56c Reviewed-on: https://go-review.googlesource.com/41819 Run-TryBot: Josh Bleecher Snyder <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 86f5f7f commit f4e5bd4

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

src/cmd/go/internal/work/build.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ See also: go install, go get, go clean.
144144
`,
145145
}
146146

147+
const concurrentGCBackendCompilationEnabledByDefault = false
148+
147149
func init() {
148150
// break init cycle
149151
CmdBuild.Run = runBuild
@@ -2252,6 +2254,12 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
22522254
if asmhdr {
22532255
args = append(args, "-asmhdr", obj+"go_asm.h")
22542256
}
2257+
2258+
// Add -c=N to use concurrent backend compilation, if possible.
2259+
if c := gcBackendConcurrency(gcflags); c > 1 {
2260+
args = append(args, fmt.Sprintf("-c=%d", c))
2261+
}
2262+
22552263
for _, f := range gofiles {
22562264
args = append(args, mkAbs(p.Dir, f))
22572265
}
@@ -2260,6 +2268,77 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
22602268
return ofile, output, err
22612269
}
22622270

2271+
// gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
2272+
func gcBackendConcurrency(gcflags []string) int {
2273+
// First, check whether we can use -c at all for this compilation.
2274+
canDashC := concurrentGCBackendCompilationEnabledByDefault
2275+
2276+
switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
2277+
case "0":
2278+
canDashC = false
2279+
case "1":
2280+
canDashC = true
2281+
case "":
2282+
// Not set. Use default.
2283+
default:
2284+
log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
2285+
}
2286+
2287+
if os.Getenv("GOEXPERIMENT") != "" {
2288+
// Concurrent compilation is presumed incompatible with GOEXPERIMENTs.
2289+
canDashC = false
2290+
}
2291+
2292+
CheckFlags:
2293+
for _, flag := range gcflags {
2294+
// Concurrent compilation is presumed incompatible with any gcflags,
2295+
// except for a small whitelist of commonly used flags.
2296+
// If the user knows better, they can manually add their own -c to the gcflags.
2297+
switch flag {
2298+
case "-N", "-l", "-S", "-B", "-C", "-I":
2299+
// OK
2300+
default:
2301+
canDashC = false
2302+
break CheckFlags
2303+
}
2304+
}
2305+
2306+
if !canDashC {
2307+
return 1
2308+
}
2309+
2310+
// Decide how many concurrent backend compilations to allow.
2311+
//
2312+
// If we allow too many, in theory we might end up with p concurrent processes,
2313+
// each with c concurrent backend compiles, all fighting over the same resources.
2314+
// However, in practice, that seems not to happen too much.
2315+
// Most build graphs are surprisingly serial, so p==1 for much of the build.
2316+
// Furthermore, concurrent backend compilation is only enabled for a part
2317+
// of the overall compiler execution, so c==1 for much of the build.
2318+
// So don't worry too much about that interaction for now.
2319+
//
2320+
// However, in practice, setting c above 4 tends not to help very much.
2321+
// See the analysis in CL 41192.
2322+
//
2323+
// TODO(josharian): attempt to detect whether this particular compilation
2324+
// is likely to be a bottleneck, e.g. when:
2325+
// - it has no successor packages to compile (usually package main)
2326+
// - all paths through the build graph pass through it
2327+
// - critical path scheduling says it is high priority
2328+
// and in such a case, set c to runtime.NumCPU.
2329+
// We do this now when p==1.
2330+
if cfg.BuildP == 1 {
2331+
// No process parallelism. Max out c.
2332+
return runtime.NumCPU()
2333+
}
2334+
// Some process parallelism. Set c to min(4, numcpu).
2335+
c := 4
2336+
if ncpu := runtime.NumCPU(); ncpu < c {
2337+
c = ncpu
2338+
}
2339+
return c
2340+
}
2341+
22632342
func (gcToolchain) asm(b *Builder, p *load.Package, obj string, sfiles []string) ([]string, error) {
22642343
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
22652344
inc := filepath.Join(cfg.GOROOT, "pkg", "include")

0 commit comments

Comments
 (0)