HN Debrief

Faster Than Ninja

  • Developer Tools
  • Programming
  • Open Source
  • Infrastructure

The post benchmarks build2 against a CMake plus Ninja setup on a C++ project and reports a small local-build win for build2 after tuning things like its file cache. That matters because Ninja is widely used as the "fast path" backend for C++ builds, so beating it at all is notable. The catch is that the comparison is not really pure executor versus executor. Several comments pointed out that CMake generation, generated-file handling, and the way the build graph gets expressed can change parallelism and hide the actual source of the gain.

If you care about build speed, profile the whole pipeline instead of treating "Ninja" as the only variable. The bigger wins are often in build graph shape, dependency scanning, and slow configure or generate steps, not the executor alone.

Discussion mood

Interested but skeptical. People like seeing a serious build-system benchmark and generally respect Ninja, but the dominant reaction was that the post over-credits build2's executor and under-explains where the measured gain actually comes from, especially given CMake's known overheads and graph-shaping quirks.

Key insights

  1. 01

    The unexplained 2 percent is the story

    A small win over Ninja only means something if you can account for it mechanically. Ninja's author argued that build2 appears to do strictly more work, which should trigger profiling and suspicion, not a victory lap. The build2 author answered with measured costs and said parallel header dependency work across 24 threads plus file cache effects can plausibly add up to about 2 percent. That exchange shifts the takeaway from "build2 beat Ninja" to "the benchmark is credible, but the causal model is still thin."

    Treat narrow benchmark wins as a debugging target. If you cannot explain a 2 percent gain in terms of actual work avoided or parallelized, do not generalize it to your own build stack.

      Attribution:
    • evmar #1
    • boris #1
  2. 02

    Generated files can quietly serialize CMake builds

    CMake plus Ninja can look fully parallel while still hiding avoidable stalls. When generated sources sit inside a broader library target, CMake may force the rest of that target to wait, even if unrelated compilation could have started earlier. That makes the benchmark partly about how the build graph is emitted, not just how fast Ninja runs it. If build2 makes those dependencies easier to express without bundling unrelated work together, that is a real advantage.

    Audit any generated-code steps in your build. Splitting generators into separate targets can unlock parallelism without changing compilers or hardware.

      Attribution:
    • Orphis #1
    • evmar #1
  3. 03

    CMake's front end is often the actual bottleneck

    The 15 second CMake generation time raised more eyebrows than the 3 second build. Comments explained that old Autotools-style probe logic can make CMake painfully slow because each check may create and build a tiny project, and CMake's single-threaded generator can scale badly on large target graphs. In some setups the generator work approaches quadratic growth because target properties get reevaluated in many dependency contexts. That means a build-system comparison can be dominated by configuration architecture before execution speed even matters.

    Measure configure and generate separately from incremental build execution. If those phases are eating minutes, switching generators will not save you. Restructuring probes and target graphs might.

      Attribution:
    • bluGill #1
    • Orphis #1
    • vient #1
  4. 04

    Performance claims need a causal theory

    Raw timings are not enough when the result runs against first-principles expectations. The criticism here was that unexpected speedups are often harness mistakes, hidden work differences, or plain noise until you can tie them to a concrete mechanism. That is a stronger standard than just publishing numbers, and it is the right one for build tooling where tiny graph changes can distort comparisons.

    When you publish or consume a benchmark, require an explanation as well as a chart. If the mechanism is vague, assume the result may not transfer to your environment.

      Attribution:
    • quotemstr #1

Against the grain

  1. 01

    The post overstates Ninja's scope problem

    The harshest pushback was not about timing but about product framing. The complaint was that the post seemed to fault Ninja for not handling dependency fetching, configuration, packaging, and other higher-level stages that many users intentionally keep separate. That changes the comparison from "fast executor" to "integrated build environment," which is a different choice with different tradeoffs. The reply was that tighter integration can enable optimizations, but the criticism stands that modular pipelines are often deliberate, not missing features.

    Decide first whether you want a composable pipeline or an all-in-one build tool. Do not let a speed benchmark smuggle in a change to your build philosophy.

      Attribution:
    • eska #1
    • Orphis #1
  2. 02

    Older build tools still win on user-facing behavior

    Several comments argued that Autotools solved a real problem newer systems still neglect. End users building from source care about discoverable configuration options, local documentation, and working on odd or old machines. Others pushed back that Autotools often fails in custom cross-compiling environments despite asking the right questions, while CMake works reliably once a proper toolchain file exists. The split is useful because it shows that "better build system" depends on whether you optimize for maintainers, developers, or downstream builders.

    Choose build tooling based on who actually runs it. Internal and product teams can optimize for developer ergonomics, but distributed source builds still need clear flags, docs, and predictable portability.

      Attribution:
    • sramsay #1
    • bluGill #1
    • wpollock #1

In plain english

Autotools
A traditional Unix build-tool suite, including configure scripts and Makefile generation, used to detect system features and prepare builds.
build graph
The dependency graph that tells a build tool which tasks depend on which files or other tasks and what can run in parallel.
build2
A C and C++ build system and package manager that aims to handle configuration, dependency management, and builds in one tool.
C++
A compiled programming language commonly used for systems software, game engines, and performance-sensitive applications.
CMake
A widely used cross-platform build configuration tool that generates project files or Ninja build files from its own build language.
configure
A build phase that detects compilers, libraries, and platform features, then decides how the project should be built.
cross-compiling
Building software on one machine or architecture to run on a different machine or architecture.
dependency scanning
The process of discovering which files, such as C and C++ header files, a source file depends on.
Ninja
A small, fast build executor designed to run a precomputed build graph quickly, usually generated by another tool such as CMake.
target
A named build output or step, such as a library, executable, or generated file, along with its dependencies.
toolchain
The set of compiler, linker, and related tools used to build software for a given platform.

Reference links

Build system references

CMake optimization work

Ninja issues