Skip to content

Commit d0b8321

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 golang#15756 Change-Id: I8a56e88953071a05eee764002024c54cd888a56c
1 parent 65bba82 commit d0b8321

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
@@ -2247,6 +2249,12 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
22472249
if asmhdr {
22482250
args = append(args, "-asmhdr", obj+"go_asm.h")
22492251
}
2252+
2253+
// Add -c=N to use concurrent backend compilation, if possible.
2254+
if c := gcBackendConcurrency(gcflags); c > 1 {
2255+
args = append(args, fmt.Sprintf("-c=%d", c))
2256+
}
2257+
22502258
for _, f := range gofiles {
22512259
args = append(args, mkAbs(p.Dir, f))
22522260
}
@@ -2255,6 +2263,77 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
22552263
return ofile, output, err
22562264
}
22572265

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

0 commit comments

Comments
 (0)