Lalit Maganti built buildprof and used it to explain most of Bun's 24 minute Zig build

Image: Lalit Maganti
Why it mattersSlow builds usually get blamed on the language or the compiler, but the recorded timeline moves the blame to a specific process and its inputs, which is the difference between "we should switch" and "we should change one flag on this library".
Perfetto engineer Lalit Maganti published buildprof on 12 September, an Apache-2.0 Linux tool that records every process a build command launches and lays them out on one timeline. He built it to answer a question about Bun. Bun 1.3.14 took 30 minutes to build on Bun's own CI, Bun 1.4.0 takes 5 minutes 37 seconds after the Rust rewrite, and Bun's chief architect had attributed the change to Rust. Maganti's replay on a 6 core, 12 thread Linux virtual machine reproduced the pair at 24 minutes 24 seconds versus 5 minutes 40 seconds. Several things changed between those two builds alongside the language, and buildprof was written to say which change did the work.
How it records a build
Put buildprof -- in front of any build command. It uses ptrace to follow the process tree of the command it launches, including subprocesses and their subprocesses, and records when each starts and ends. A seccomp filter intercepts only the file syscalls it needs, so it can also draw arrows between a process that reads a file and the process that wrote it. On ripgrep the recording overhead is about 130 milliseconds on a 12 second build; on Redis it adds 5 seconds to a 27 second build. The overhead is turned off with --no-file-events.
What the Bun trace showed
The Zig build's ld.lld linker invocation ran alone at the end for 16 minutes 35 seconds, two thirds of the total. Turning on --compiler-traces, which pulls in LLD's own timing events, showed that the linker was in Full Link Time Optimization: it was running the compiler's optimization and code generation passes across the whole program, and one pass called OptModule took just over 10 minutes on its own. The Rust build did LTO too, but ThinLTO, and its link took 2 minutes 24 seconds.
Changing Bun's Zig build to ThinLTO only got the link down to nearly 13 minutes, because the linker's inputs included libraries from a separate WebKit build that was still compiled with -flto=full. Rebuilding WebKit and its ICU dependency with ThinLTO cut the link to 7 minutes 22 seconds and the whole Zig build to 15 minutes 11 seconds.
What was left after that
Seven of those 15 minutes still passed before the linker started. Following the file arrows back showed the linker waiting on bun-zig.o from the Zig branch of the build. That is where Maganti stopped tracing and drew the structural conclusion: Bun's Rust rewrite compiles as more than 90 crates that can run in parallel, and the Zig build compiled everything through a single Zig module that could not. The linker being asked to optimize one large ThinLTO bitcode module rather than the same work spread across crates is his suggested explanation for the remaining gap, unproven.
Build times are usually reported as a single number and the change is attributed to whichever big thing moved. The trace makes that harder: the same recording that reproduced Jarred Sumner's 5x claim also shows that most of the original gap came from one linker flag on a downloaded WebKit library. For a team looking at its own long build, buildprof answers a specific question ("what was the linker waiting for at minute 10") without needing to instrument every subprocess by hand.
Source
Blog post: I made a build visualizer to understand Bun's compile times by Lalit Maganti. Repository: LalitMaganti/buildprof on GitHub.
Source: Lalit Maganti
This item was written by an AI system from the linked source. Reveneau is responsible for what it publishes.
Get AI News in your inbox
New developer tools, model and agent releases, and how teams are actually shipping with them. Short, and only when there is something worth reading.

