-
Notifications
You must be signed in to change notification settings - Fork 0
Speeding 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 (note the patch is against go1.6rc2
, but it will likely patch cleanly against any go1.6
version):
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"
- Home
- Beta Dashboard
- Release Process
- [Building and running tests](Building and running tests)
- [Using
musl
to get a completely static binary](Usingmusl
to get a completely static binary)
- [Using
-
Productivity
- [Multiple GOPATHs](Multiple GOPATHs)
- [Ben's Go Emacs setup](Ben's Go Emacs setup)
- [Radu's vim setup](Radu's vim setup)
- [Speeding up cgo builds](Speeding up cgo builds)
- [Rando scripts](Rando scripts)
- [Life of a transaction](Life of a transaction)
- Readings