`,
}
+const concurrentGCBackendCompilationEnabledByDefault = false
+
func init() {
// break init cycle
CmdBuild.Run = runBuild
if asmhdr {
args = append(args, "-asmhdr", obj+"go_asm.h")
}
+
+ // Add -c=N to use concurrent backend compilation, if possible.
+ if c := gcBackendConcurrency(gcflags); c > 1 {
+ args = append(args, fmt.Sprintf("-c=%d", c))
+ }
+
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
}
return ofile, output, err
}
+// gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
+func gcBackendConcurrency(gcflags []string) int {
+ // First, check whether we can use -c at all for this compilation.
+ canDashC := concurrentGCBackendCompilationEnabledByDefault
+
+ switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
+ case "0":
+ canDashC = false
+ case "1":
+ canDashC = true
+ case "":
+ // Not set. Use default.
+ default:
+ log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
+ }
+
+ if os.Getenv("GOEXPERIMENT") != "" {
+ // Concurrent compilation is presumed incompatible with GOEXPERIMENTs.
+ canDashC = false
+ }
+
+CheckFlags:
+ for _, flag := range gcflags {
+ // Concurrent compilation is presumed incompatible with any gcflags,
+ // except for a small whitelist of commonly used flags.
+ // If the user knows better, they can manually add their own -c to the gcflags.
+ switch flag {
+ case "-N", "-l", "-S", "-B", "-C", "-I":
+ // OK
+ default:
+ canDashC = false
+ break CheckFlags
+ }
+ }
+
+ if !canDashC {
+ return 1
+ }
+
+ // Decide how many concurrent backend compilations to allow.
+ //
+ // If we allow too many, in theory we might end up with p concurrent processes,
+ // each with c concurrent backend compiles, all fighting over the same resources.
+ // However, in practice, that seems not to happen too much.
+ // Most build graphs are surprisingly serial, so p==1 for much of the build.
+ // Furthermore, concurrent backend compilation is only enabled for a part
+ // of the overall compiler execution, so c==1 for much of the build.
+ // So don't worry too much about that interaction for now.
+ //
+ // However, in practice, setting c above 4 tends not to help very much.
+ // See the analysis in CL 41192.
+ //
+ // TODO(josharian): attempt to detect whether this particular compilation
+ // is likely to be a bottleneck, e.g. when:
+ // - it has no successor packages to compile (usually package main)
+ // - all paths through the build graph pass through it
+ // - critical path scheduling says it is high priority
+ // and in such a case, set c to runtime.NumCPU.
+ // We do this now when p==1.
+ if cfg.BuildP == 1 {
+ // No process parallelism. Max out c.
+ return runtime.NumCPU()
+ }
+ // Some process parallelism. Set c to min(4, numcpu).
+ c := 4
+ if ncpu := runtime.NumCPU(); ncpu < c {
+ c = ncpu
+ }
+ return c
+}
+
func (gcToolchain) asm(b *Builder, p *load.Package, obj string, sfiles []string) ([]string, error) {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(cfg.GOROOT, "pkg", "include")