29

I often see the claim made that Ninja is faster than Make, is better at supporting incremental builds, and better at parallelization. Is this a quality of implementation issue or is there something in the Ninja language that enables this?

I understand that Ninja and Make use different file formats to describe the dependency graph of tasks. I understand that Make allows the use of more high level features, such as globs, than ninja. If such high-level features are used then Make performs a more complex task than Ninja and we cannot expect Make to be faster. This is an unfair comparison.

However, assume that such high level features are not used. No globing, no pattern rules, just basic "out_file: in_file1, in_file2\n\tcommand to build" repeated over and over and over. Not using such high-level feature evens the playing field between Make and Ninja in terms of the task that they perform.

My understanding is that, if we limit Makefiles in such a way, that Ninja files and Makefiles can easily be transformed into each other. Is this correct?

Is there any inherent reason why Make executed on limited Makefiles is any slower than Ninja? or is it simply case of the standard Make implementations not being optimized for Makefiles that are structured in this way?

B.S.
  • 1,435
  • 2
  • 12
  • 18
  • 5
    I am more confused about this. Basically compiling is usually much more consuming than parsing a Makefile/build.ninja. If the majority of the time is spent on compiling, how Ninja provide better performance? – xis Aug 01 '21 at 07:17

1 Answers1

12

In short, Ninja parses faster, and has built-in features that reduce the amount to parse.

From the philosophical overview in the ninja manual:

Where other build systems are high-level languages, Ninja aims to be an assembler.

Ninja files are often "compiled" from other makefiles, making it a two-step process whereas Make is a single step. The two-step can be slower than plain Make. Because only the second step is necessary for most the time the net effect is it being faster. It's somewhat similar to comparing a compiled and interpreted program.

Ninja add several features to Make. Implementing these in a makefile lead to longer, more complicated makefiles and thus much more parsing.

Andreas
  • 5,086
  • 3
  • 16
  • 36
  • 2
    Just to note, buffered output has been available in GNU make since release 4.0 (2013) and parallelism has been available since forever of course. These are not on by default but they are command-line options (that can be enabled by default via environment variables) so they don't cost any make build time. And, edges with multiple outputs on explicit rules (implicit rules with multiple outputs have been supported forever) has built-in support starting with GNU make 4.3. But, I don't disagree with the general points made in that section of the Ninja manual. – MadScientist Jul 11 '20 at 21:32
  • You can get multiple outputs in GNU make with the grouped output syntax: "foo.a foo.so &: foo.cpp bar.cpp". See https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html for details. – B.S. Jul 15 '20 at 16:00
  • From what you and @MadScientist write, I gather that the ninja language is indeed equivalent to a subset of GNU make, if a sufficiently new version is used to have grouped output. Is this correct? – B.S. Jul 15 '20 at 16:03
  • 1
    I'm not really familiar with everything Ninja. I believe that Ninja provides some type of built-in dependency generating (automatically figuring out which headers are included by which source files for examples), which GNU make doesn't have built-in (this can be done cheaply if you have a modern compiler like GCC or clang but you have to do it yourself). Also, Ninja automatically rebuilds if command lines have changed, which GNU make does not have. Ninja maintains a database of information about the last build; GNU make keeps no state about any previous build. – MadScientist Jul 15 '20 at 18:18
  • 4
    The Ninja dependency support consists of calling `gcc -M`, just as Make does, and then converting the output of GCC, which is a make rule, to a ninja rule. Formulated otherwise, it solves a problem that you would not have in the first place, if you had not used Ninja. – B.S. Jul 16 '20 at 06:59