I've pushed an experimental branch that adds a new feature to make dependency loading much faster:https://github.com/martine/ninja/tree/dep-pipeI haven't merged it yet since there are a few details to work out, particularly the build-time syntax.The basic idea is this: rather than have subcommands write out depfiles when they run and load these files on subsequent Ninja runs, instead greedily parse the dependency output as it's written and store it in a compact format for quicker loading on the next Ninja run.On Unix this means telling gcc to output its dependency information via /dev/fd/$NINJA_DEPS, which is a pipe specially set up in subcommands to be read and parsed by Ninja. On Windows, we already had code to parse the /showIncludes output from MSVC, so no pipes are needed and we can drop the dependency helper.Streaming all this information into the Ninja process during the build allows a very compact format, which is quick to load by itself and especially saves time on Windows because all dependency information can be loaded from a single file. The file format, briefly, is a sequence of records: either strings (paths), or dependency lists, which are sets of integers that refer to the paths.The format (which is internal to Ninja and only interesting to look at if you'd like to comment on data structures) is documented further here:I haven't run a Chrome build yet but I estimate it's about 40x less data to load. I also verified the code to work with a CMake-generated Clang build (where I manually hacked up the resulting build files to trigger the new behavior), where it was faster but not in an important way (on my laptop, no-op Clang builds are a handful of milliseconds either way).
There are a few open questions that I'd like feedback on.One is how to specify in the build file that a command supports this special behavior. It seems reasonable to me to keep the "old" (depfile-parsing-based) behavior around for project that have special needs in how their dependencies work. So now there are three modes for dependencies: depfiles, the pipe used on Unix, and MSVC-specific /showIncludes parsing for Windows. It seems these could be selected via a per-rule (or per-build) attribute, but I'm not sure what to call it. "deps=msvc" and "deps=pipe"? (It seems weird to call the Unix one gcc-specific since it works with other compilers etc.)
Second, right now as designed this only works for commands that have a single output. That behavior is the reality for 99% of the code Ninja builds (C code) so maybe it's ok to use depfiles for the rest of cases, but it's also maybe appealing to have a single unified format for all dependencies.
I've pushed an experimental branch that adds a new feature to make dependency loading much faster:https://github.com/martine/ninja/tree/dep-pipeI haven't merged it yet since there are a few details to work out, particularly the build-time syntax.
I don't have an opinion on either of the questions you wanted feedback
on. I'll keep them in mind as I keep working but for now, any of the
ideas you suggested seem fine to me.
What I can share is some numbers. I work on a fairly large linux based
project and my coworkers typically build off nfs mounts. When the caches
are cold and the network traffic is high, loading the depfiles can take
a significant amount of time. I have seen the 'depfile load' line in the
'-d stats' go above 40000 (yep, thats 40 seconds). I tried to do a fair
comparison by rebooting and then running noop builds in two different
build trees. They both have the same source tree however so I ran
dep-pipe first to give it a disadvantage. Here are the results.
dep-pipe:
...
node stat 4554 571.9 2604.6
master:
...
node stat 5544 329.0 1823.8
...
and there are 20%
less files to stat.
Should there be less files to stat? (Shouldn't the nodes already have been unique? Maybe stronger normalization? Or am I being thick?)
I thought it was because ninja did not have to stat all the *.d files but this does not add up. I counted 1592 *.d files when I built with master and 0 when built with dep-pipe. Also, now that I think about it, Im not sure it makes sense to stat the *.d files anyways.
In theory, if a depfile is more recent when the target then target should be rebuilt for safety. That covers the case where a previous incremental build has been terminated leaving an incomplete .d file (missing relevant dependencies).
But as I remember ninja only stats depfiles for targets with 'restat' enabled. Is this your case?
The numbers may not add up because it's not always necessary to stat all dependencies to conclude that a target is out of date. Such if a single input is found to be out of date, it's not necessary to stat the rest of inputs.
dep-pipe:
ninja: no work to do.
metric count avg (us) total (ms)
.ninja parse 2 36260.5 72.5
canonicalize str 34208 0.3 8.6
canonicalize path 34208 0.1 4.2
lookup node 39095 0.2 5.9
.ninja_log load 1 18496.0 18.5
.ninja_deps load 1 148398.0 148.4
node stat 4554 571.9 2604.6
path->node hash load 0.96 (5882 entries / 6151 buckets)
Thanks for sharing. Your build system has made my work fun again!
Would you mind telling me the size of the $builddir/.ninja_deps file?NFS causes latency when opening files so I would expect it to be slower, but in your numbers above it's nearly 10x slower than opening the build log, wihch is also a single file. I guess the difference must be in the relative sizes of the files?
PS: if you're still using this branch, you might want to keep pulling down new code as I'm still finishing it and fixing bugs. It also currently does the wrong thing if your build is interrupted at the wrong time, so I wouldn't trust it yet.
Would you mind telling me the size of the $builddir/.ninja_deps file?NFS causes latency when opening files so I would expect it to be slower, but in your numbers above it's nearly 10x slower than opening the build log, wihch is also a single file. I guess the difference must be in the relative sizes of the files?
.ninja_deps 1001K
.ninja_log 217K
On Unix this means telling gcc to output its dependency information via /dev/fd/$NINJA_DEPS, which is a pipe specially set up in subcommands to be read and parsed by Ninja.
To summarize, your build is 4.5k files and 1mb deps. I had estimated that Chrome (~35k files, so 8x larger) should only be about 2mb of deps, so that file still seems a bit big. I *think* it is this bug I fixed after my announcement:so it should just fix itself, so I'm not gonna worry about it more than writing this mail where I'm trying to convince myself it'll be fine. :)If you wouldn't mind one more stat, could you "time cat path/to/builddir/.ninja_deps > /dev/null" to estimate how long it takes to read the file via NFS? (NFS has been a recurring nemesis in my career, it's funny to see it turn up here.)
On Mon, Jan 7, 2013 at 1:27 PM, Frank Miller <christopher...@gmail.com> wrote:
...
dep-pipe:...node stat 4554 571.9 2604.6
master:
...node stat 5544 329.0 1823.8...
and there are 20%
less files to stat.Should there be less files to stat? (Shouldn't the nodes already have been unique? Maybe stronger normalization? Or am I being thick?)
If you are building on linux, I can suggest running both variants under 'strace -estat64', and compare the traces.
--
You received this message because you are subscribed to the Google Groups "ninja-build" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ninja-build...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
For a quick test you can probably just hack out the showincludes parsing from msvc-helper. If it passes the output through to ninja then everything else should work. I was hoping you had a bright idea about env stuff. :) maybe just integrate it into ninja vars I guess.
brevity due to phone