@@ -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
@@ -2247,6 +2249,12 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
2247
2249
if asmhdr {
2248
2250
args = append (args , "-asmhdr" , obj + "go_asm.h" )
2249
2251
}
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
+
2250
2258
for _ , f := range gofiles {
2251
2259
args = append (args , mkAbs (p .Dir , f ))
2252
2260
}
@@ -2255,6 +2263,77 @@ func (gcToolchain) gc(b *Builder, p *load.Package, archive, obj string, asmhdr b
2255
2263
return ofile , output , err
2256
2264
}
2257
2265
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
+
2258
2337
func (gcToolchain ) asm (b * Builder , p * load.Package , obj string , sfiles []string ) ([]string , error ) {
2259
2338
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
2260
2339
inc := filepath .Join (cfg .GOROOT , "pkg" , "include" )
0 commit comments