Skip to content

Slow parallel C compilation #25203

@darkestpigeon

Description

@darkestpigeon

Nim Version

Nim Compiler Version 2.3.1 [Linux: amd64]
Compiled at 2025-10-03
Copyright (c) 2006-2025 by Andreas Rumpf

git hash: 483389d3999e243d7c61647c55187b0578dc57ee
active boot switches: -d:release

Also on stable and devel releases

Description

The parallel compilation of .c files is slower that it should be when there are many files. (For my project, it is ~30 seconds vs ~4 seconds when executing the same commands outside of nim)

I'm on Ubuntu 24.04, gcc 13.3.0

Here is an example project (depends on chronos and websock):

# test.nim
import std/uri

import chronos
import chronicles
import websock/websock


proc main() {.async.} =
  let ws = WebSocket.connect(parseUri("ws://localhost:8080"))

waitFor main()

Here is instrumentation:

diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index e6e35f462..8232b1609 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -993,7 +993,29 @@ proc callCCompiler*(conf: ConfigRef) =
       script.add("\n")
   
   if optCompileOnly notin conf.globalOptions:
+    for cmd in cmds:
+      echo cmd
+
+    block:
+      let tStart = getTime()
+      let p = startProcess(
+        "xargs -I{} -P$1 sh -c '{}'" % [$countProcessors()],
+        options={poEvalCommand})
+      let inp = p.inputStream
+      for cmd in cmds:
+        inp.writeLine(cmd)
+        inp.flush()
+      inp.close()
+      let ret = p.waitForExit()
+      let tEnd = getTime()
+      let dt = 1e-3*(tEnd - tStart).inMilliseconds.float
+      stderr.writeLine ">>> compiled in ", dt.formatFloat(ffDecimal, 3)
+
+    let tStart = getTime()
     execCmdsInParallel(conf, cmds, prettyCb)
+    let tEnd = getTime()
+    let dt = 1e-3*(tEnd - tStart).inMilliseconds.float
+    stderr.writeLine ">>> compiled in ", dt.formatFloat(ffDecimal, 3)
   if optNoLinking notin conf.globalOptions:
     # call the linker:
     var objfiles = ""

With this, run

nim c -f test.nim > cmds.txt

This will print both measured compiled times and save the commands to a text file.

Current Output

...
>>> compiled in 0.690
CC: ...
...
>>> compiled in 1.616
...

Expected Output

...
>>> compiled in 0.690
CC: ...
...
>>> compiled in 0.690
...

Known Workarounds

No response

Additional Information

Ubuntu 24.04, gcc 13.3.0

I've tried calling execProcesses directly on the saved commands:

import osproc, times, strformat

var cmds: seq[string]

for line in lines("cmds.txt"):
  cmds.add(line)

let tStart = getTime()

let ret = execProcesses(
  cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, n=countProcessors())

if ret == 0:
  let tEnd = getTime()
  let dt = 1e-3*((tEnd - tStart).inMilliseconds).float
  echo "compiled in {dt:.3f} s".fmt
else:
  echo "failed"

This gives the same timing as xargs. While the same execProcesses call placed instead of xargs in the compiler takes much more time.

Out of ideas what could be the problem for now.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions