NixOS: Systemd unit-linking script rewrite for 60x speedups by Profpatsch · Pull Request #479442 · NixOS/nixpkgs

2 min read Original article ↗

@Profpatsch

The systemd unit linking script has been a thorn in my side
for a while now. It has to be rebuilt every single time any
unit dependency in a system closure changes, and due to the
multiple for-loops, we’d see multi-second build times that
were essentially caused by spawning thousands of ln/cp/etc
processes one after the other.

An example from a server (small-ish deployment) configuration:

building '/nix/store/2lz2v7dfqvc6azih4ppriv0b40c6gwif-system-units-bash-legacy.drv'...
Built system-units with bash in 2788ms
building '/nix/store/r4xqf8g4aikg2ihr0psvnwa6kj1nzci4-user-units-bash-legacy.drv'...
Built user-units with bash in 193ms
building '/nix/store/qir3gc3la4fndpi45c226q9ysl5qs28v-system-units-go-ng.drv'...
Built system-units with Go in 79ms (consider enabling systemd.unitGenerator.useGoImpl)
building '/nix/store/fah3ipi5iwd44fj5q1ypm3r8zd94mcc9-user-units-go-ng.drv'...
Built user-units with Go in 18ms (consider enabling systemd.unitGenerator.useGoImpl)

As you can see the new scripts take 30–60 times less time to run
and are for all intents and purposes is instant, even without
and paralellization. This speedup is purely from replacing
`exec` with direct syscalls.

The default configuration after this change is optimized
for debugging the new implementation, it will run both
the bash and the go version and compare their outputs.
In case something is different, it displays a warning and
asks the user to open a new issue with the verbose output.
By default, the old bash script output is used.

There is a new option `systemd.unitGenerator.useGoImpl`
which, when enabled, does not run the old bash scripts
anymore and will give the speedup. This should be flipped
in future versions of nixos, and the bash implementation
dropped.

Further advantages of the new script is exhaustive error
handling, and a verbose debugging mode that can be enabled
with `systemd.unitGenerator.debug = true;`.