Skip to content

Commit

Permalink
Use jobs flag to really control number of concurrent compilations
Browse files Browse the repository at this point in the history
The issue was due to the peculiar way concurrency and parallelism are handled in go.
We used to set GOMAXPROC to 1 to avoid parallelizing the WaitGroup execution.
This would work, in theory, unless the goroutines sleep.
In that case, another goroutine is allowed to start concurrently (only 1 goroutine running in parallel, so GOMAXPROC is happy).
Since our goroutines sleep (wait) after calling gcc, another task is started, without any hard limit, till the WaitGroup is completely spawned.
On systems with limited resources (as RaspberryPi) and cores with lots of files this can trigger an out of memory condition.
  • Loading branch information
facchinm authored and cmaglie committed Aug 26, 2019
1 parent f034233 commit 764fa09
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 12 deletions.
7 changes: 7 additions & 0 deletions commands/compile/compile.go
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"io"
"path/filepath"
"runtime"
"sort"
"strings"

Expand Down Expand Up @@ -115,6 +116,12 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W

builderCtx.CoreBuildCachePath = paths.TempDir().Join("arduino-core-cache")

jobs := runtime.NumCPU()
if req.GetJobs() > 0 {
jobs = int(req.GetJobs())
}
builderCtx.Jobs = jobs

builderCtx.USBVidPid = req.GetVidPid()
builderCtx.WarningsLevel = req.GetWarnings()

Expand Down
34 changes: 22 additions & 12 deletions legacy/builder/builder_utils/utils.go
Expand Up @@ -175,20 +175,30 @@ func compileFilesWithRecipe(ctx *types.Context, sourcePath *paths.Path, sources

ctx.Progress.Steps = ctx.Progress.Steps / float64(len(sources))
var wg sync.WaitGroup
wg.Add(len(sources))

for _, source := range sources {
go func(source *paths.Path) {
defer wg.Done()
PrintProgressIfProgressEnabledAndMachineLogger(ctx)
objectFile, err := compileFileWithRecipe(ctx, sourcePath, source, buildPath, buildProperties, includes, recipe)
if err != nil {
errorsChan <- err
} else {
objectFilesChan <- objectFile
// Split jobs into batches of N jobs each; wait for the completion of a batch to start the next
par := ctx.Jobs

go func() {
for total := 0; total < len(sources); total += par {
for i := total; i < total+par && i < len(sources); i++ {
wg.Add(1)
go func(source *paths.Path) {
defer wg.Done()
PrintProgressIfProgressEnabledAndMachineLogger(ctx)
objectFile, err := compileFileWithRecipe(ctx, sourcePath, source, buildPath, buildProperties, includes, recipe)
if err != nil {
errorsChan <- err
} else {
objectFilesChan <- objectFile
}
}(sources[i])
}
}(source)
}
wg.Wait()
}

doneChan <- struct{}{}
}()

go func() {
wg.Wait()
Expand Down
3 changes: 3 additions & 0 deletions legacy/builder/types/context.go
Expand Up @@ -111,6 +111,9 @@ type Context struct {
// Experimental: use arduino-preprocessor to create prototypes
UseArduinoPreprocessor bool

// Parallel processes
Jobs int

// Out and Err stream to redirect all Exec commands
ExecStdout io.Writer
ExecStderr io.Writer
Expand Down

0 comments on commit 764fa09

Please sign in to comment.