Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upSpeeding up cgo builds
The go tool is used to build all of the CockroachDB source, including C and C++ dependencies via the cockroachdb/c-{rocksdb,snappy,protobuf} packages. Unfortunately, go build does not parallelize building of source files within a package. For .go files this isn't a problem because the Go compiler is fast. For C and C++ files the issue is more noticeable. Compiling the c-rocksdb package takes ~2min on my machine. Parallelizing the compilation of C and C++ files by go build reduces the build time to 30sec. If you frequently edit any of the cockroachdb/c-* packages and you're comfortable running a hacked version of the go tool, this patch is for you:
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index a1f925e..8ca739c 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -692,6 +692,8 @@ type builder struct {
exec sync.Mutex
readySema chan bool
ready actionQueue
+
+ tasks chan func()
}
// An action represents a single action in the action graph.
@@ -1234,6 +1236,7 @@ func (b *builder) do(root *action) {
}
b.readySema = make(chan bool, len(all))
+ b.tasks = make(chan func(), buildP)
// Initialize per-action execution state.
for _, a := range all {
@@ -1310,6 +1313,8 @@ func (b *builder) do(root *action) {
a := b.ready.pop()
b.exec.Unlock()
handle(a)
+ case task := <-b.tasks:
+ task()
case <-interrupted:
setExitStatus(1)
return
@@ -3123,12 +3128,16 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
}
+ var tasks []func()
+ var results chan error
+
cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
for _, cfile := range cfiles {
+ cfile := cfile
ofile := obj + cfile[:len(cfile)-1] + "o"
- if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil {
- return nil, nil, err
- }
+ tasks = append(tasks, func() {
+ results <- b.gcc(p, ofile, cflags, obj+cfile)
+ })
linkobj = append(linkobj, ofile)
if !strings.HasSuffix(ofile, "_cgo_main.o") {
outObj = append(outObj, ofile)
@@ -3136,35 +3145,65 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
}
for _, file := range gccfiles {
+ file := file
ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
- if err := b.gcc(p, ofile, cflags, file); err != nil {
- return nil, nil, err
- }
+ tasks = append(tasks, func() {
+ results <- b.gcc(p, ofile, cflags, file)
+ })
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
for _, file := range gxxfiles {
+ file := file
// Append .o to the file, just in case the pkg has file.c and file.cpp
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
- if err := b.gxx(p, ofile, cxxflags, file); err != nil {
- return nil, nil, err
- }
+ tasks = append(tasks, func() {
+ results <- b.gxx(p, ofile, cxxflags, file)
+ })
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
for _, file := range mfiles {
+ file := file
// Append .o to the file, just in case the pkg has file.c and file.m
ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
- if err := b.gcc(p, ofile, cflags, file); err != nil {
- return nil, nil, err
- }
+ tasks = append(tasks, func() {
+ results <- b.gcc(p, ofile, cflags, file)
+ })
linkobj = append(linkobj, ofile)
outObj = append(outObj, ofile)
}
+ // Give the results channel enough capacity so that sending the
+ // result is guaranteed not to block.
+ results = make(chan error, len(tasks))
+
+ // Feed the tasks into the b.tasks channel on a separate goroutine
+ // because the b.tasks channel's limited capacity might cause
+ // sending the task to block.
+ go func() {
+ for _, task := range tasks {
+ b.tasks <- task
+ }
+ }()
+
+ // Loop until we've received results from all of our tasks or an
+ // error occurs.
+ for count := 0; count < len(tasks); {
+ select {
+ case err := <-results:
+ if err != nil {
+ return nil, nil, err
+ }
+ count++
+ case task := <-b.tasks:
+ task()
+ }
+ }
+
linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o"
pie := (goarch == "arm" && goos == "linux") || goos == "android"