Parallel build
Parallel build#
The program and the reachable part of std form one LLVM module. After optimisation (single-threaded) the module is cut into
codegen units: each unit is compiled to its own object file on its own thread, and all objects go to the linker. This applies to
every command that builds a program: oxl build, oxl run, oxl test.
Units and threads are different things#
| What | Flag | Environment | oxl.toml | Changes the output |
|---|---|---|---|---|
| Number of units | --codegen-units N (1..256) | OXL_CODEGEN_UNITS | [profile.<name>] codegen-units = N | yes |
| Number of threads | -j N / --jobs N | OXL_JOBS | — | no |
| Linker | --linker auto|lld|bfd|gold|default|<path> | OXL_LD | — | yes (another file) |
Precedence: flag, environment, oxl.toml, automatic choice.
- Reproducibility. The binary depends on the number of units only, never on
-j, the number of cores or thread scheduling: the module is split and serialised in a fixed order, only the code generation of the finished parts runs in parallel, and the objects reach the linker in unit order.-j1and-j8builds with the same units are byte-identical (D-1403, D-9001). - Automatic choice. Without an explicit number the units depend on the module content only: one unit per ~5000 optimised
instructions, at most 8. A small program (
examples/hello) is one unit, as before. The number of cores never matters, so every machine produces the same file. - Local symbols of the module become external with
hiddenvisibility when it is split; none of them is exported from the executable. - A profile with
lto = trueand--emit-llvmare not split: LTO stays one module (the object is bitcode, lld optimises). - macOS: always one unit (the Mach-O debug map and the reproducible UUID need one object); the flags are accepted and ignored.
- Cost: debug info and metadata are duplicated between the parts, so the binary is slightly larger (see the section on parallel
builds in the benchmarks). For the fastest and smallest binary use
--codegen-units 1.
Linker#
When ld.lld (or ld.lld-18) is in PATH, oxl links with -fuse-ld=lld: about 50 ms instead of about 160 ms with GNU ld. Without
lld everything works as before (the driver's own linker is used silently). An automatically chosen lld that fails is replaced by the
default linker and the link is retried. --linker bfd (or OXL_LD=bfd) forces GNU ld, --linker default passes no -fuse-ld,
--linker /path/to/ld uses your linker. oxl doctor shows the choice. The linker is part of the build cache key.
Build cache#
The local build cache key (OXL_BUILD_CACHE=1, D-6313) contains the number of units and the linker, not the number of threads.
Tests#
oxl test accepts --codegen-units N and --linker; build threads come from OXL_JOBS (-j of oxl test is the number of
tests running at once). Workspace members are built one after another, each build is parallel inside.