@@ -144,6 +144,8 @@ See also: go install, go get, go clean.
144
144
` ,
145
145
}
146
146
147
+ const concurrentGCBackendCompilationEnabledByDefault = false
148
+
147
149
func init () {
148
150
// break init cycle
149
151
CmdBuild .Run = runBuild
@@ -2252,6 +2254,12 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
2252
2254
if asmhdr {
2253
2255
args = append (args , "-asmhdr" , obj + "go_asm.h" )
2254
2256
}
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
+
2255
2263
for _ , f := range gofiles {
2256
2264
args = append (args , mkAbs (p .Dir , f ))
2257
2265
}
@@ -2260,6 +2268,77 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
2260
2268
return ofile , output , err
2261
2269
}
2262
2270
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
+
2263
2342
func (gcToolchain ) asm (b * Builder , p * load.Package , obj string , sfiles []string ) ([]string , error ) {
2264
2343
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
2265
2344
inc := filepath .Join (cfg .GOROOT , "pkg" , "include" )
0 commit comments