Skip to content

Share evaluation caches across installables#10570

Merged
edolstra merged 3 commits intoNixOS:masterfrom
layus:shared_caches
Apr 26, 2024
Merged

Share evaluation caches across installables#10570
edolstra merged 3 commits intoNixOS:masterfrom
layus:shared_caches

Conversation

@layus
Copy link
Member

@layus layus commented Apr 20, 2024

Motivation

I noticed that building multiple installables from the same flake did not share
the same root Value. This has significant performance drawbacks that this PR
addresses by reusing said value for all the attribute paths accessed in the
same source.

Nix takes 4 minutes to evaluate (create the .drv files for) 276 packages from
the same flake, which amounts to .9s per installable. With this patch, the
whole evaluation takes 11s. That is twenty times faster. Of course, the overall
speedup is related to the amount of packages and the numbers here are very
specific and imprecise. But still.

This needs a proper review because I have no idea if this is the right approach.
Ideally, I would like the evaluation cache to be shared. Not cached and retrieved.

[edit: moved to #10590]
As this opens the gate to parallel evaluation for even better speedups, I also
discovered that our database transactions span the whole duration of the
evaluation, which became even longer now that the database is kept during the
evaluation of all the installables. I have tried to reduce the scope of the
transactions given that it is a cache. Not sure if it has any impact at all.
[/edit]

This has been extensively tested already, so at least it works as expected.

Note that this has no impact on the racy behavior of path: installables.
Since the code fetches them for every occurrence, they still can end up with
a different hash and a different cache. Arguably, a better implementation like
the once outlined above would address this by loading each source only once.

Fixes #9724

Context

Priorities and Process

Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@layus layus changed the title iShare evaluation caches across installables Share evaluation caches across installables Apr 20, 2024
@roberth
Copy link
Member

roberth commented Apr 21, 2024

This may resolve #9724

@roberth roberth added new-cli Relating to the "nix" command performance labels Apr 21, 2024
@layus layus marked this pull request as ready for review April 22, 2024 16:28
@layus layus requested a review from edolstra as a code owner April 22, 2024 16:28
@layus
Copy link
Member Author

layus commented Apr 22, 2024

CI timeout 😿

@edolstra
Copy link
Member

LGTM, however it's a workaround for the underlying issue, which is that we don't memoize evaluation across installable. If I understand it correctly, this PR only fixes it for the case where the flake has a fingerprint (e.g. dirty Git workdirs don't) and pure evaluation and the eval cache are enabled. Ideally we would merge together installables for the same flake somehow so that they share a RootValue.

@layus
Copy link
Member Author

layus commented Apr 24, 2024

LGTM, however it's a workaround for the underlying issue, which is that we don't memoize evaluation across installable. If I understand it correctly, this PR only fixes it for the case where the flake has a fingerprint (e.g. dirty Git workdirs don't) and pure evaluation and the eval cache are enabled. Ideally we would merge together installables for the same flake somehow so that they share a RootValue.

Yes, exactly.

I did not want to spend too much time on that feature, and I also did not want to enter bike-shedding area.
Because if we share the same RootValue across impure installables, then it changes the order of evaluation of installable sources, which is observable in odd ways.

There are plenty of such unsolved questions:

  1. For file: and the like, files might change during the evaluation. Right now the referenced path is copied to the store for each installable. Do we want to assume that the same name always refers to the same RootValue ? To some extent it might also be true of flakes from the registry if the registry changes during evaluation.
  2. Then someone will want different installable sources names that resolve to the same underlying nix expression/store path to also share the same RootValue.
  3. I am not yet convinced that impure evaluation may not wreak havoc in the caching logic.
  4. The lazy-tree branch may be totally incompatible with this.
  5. And probably way more questions like that that I do not want to take a decision about.

So yeah, this is an imcomplete workaround, but at least I know this one is safe 😅 .

That being said, if there is a clear idea of how this must be implemented, I am ready to do it 💪

layus and others added 2 commits April 25, 2024 00:44
@edolstra edolstra merged commit de51e5c into NixOS:master Apr 26, 2024
@layus layus deleted the shared_caches branch April 29, 2024 19:30
@tomberek
Copy link
Contributor

@layus Amazing... thanks!

@kjeremy
Copy link
Contributor

kjeremy commented May 2, 2024

Can we get a backport for this?

tebowy pushed a commit to tebowy/nix that referenced this pull request Jul 11, 2024
Share evaluation caches across installables

Before:

$ rm -rf ~/.cache/nix && time -f '%E' nix build --dry-run \
  'nixpkgs#hello' \
  'nixpkgs#clang' \
  'nixpkgs#cargo' \
  'nixpkgs#rustup' \
  'nixpkgs#bear' \
  'nixpkgs#firefox' \
  'nixpkgs#git-revise' \
  'nixpkgs#hyperfine' \
  'nixpkgs#curlie' \
  'nixpkgs#xz' \
  'nixpkgs#ripgrep'
0:03.61

After:

$ rm -rf ~/.cache/nix && time -f '%E' nix build --dry-run \
  'nixpkgs#hello' \
  'nixpkgs#clang' \
  'nixpkgs#cargo' \
  'nixpkgs#rustup' \
  'nixpkgs#bear' \
  'nixpkgs#firefox' \
  'nixpkgs#git-revise' \
  'nixpkgs#hyperfine' \
  'nixpkgs#curlie' \
  'nixpkgs#xz' \
  'nixpkgs#ripgrep'
0:01.46

This could probably use a more proper benchmark...

Fixes NixOS#313

(cherry picked from commit de51e5c)
Change-Id: I9350bebd462b6af12c51db5bf432321abfe84a16
kognise added a commit to kognise/nix that referenced this pull request Jul 11, 2024
- Fix eval cache not being persisted in `nix develop` (since NixOS#10570)
- Fallback to flake path so the eval cache isn't disabled for flakes evaluated by a relative `path:` (since NixOS#10149, NixOS#10176)
- Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases
- Drive-by: trivial typo fix
kognise added a commit to kognise/nix that referenced this pull request Jul 12, 2024
- Fix eval cache not being persisted in `nix develop` (since NixOS#10570)
- Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases
- Drive-by: trivial typo fix
github-actions bot pushed a commit that referenced this pull request Jul 26, 2024
- Fix eval cache not being persisted in `nix develop` (since #10570)
- Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases
- Drive-by: trivial typo fix

(cherry picked from commit e764ed3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-cli Relating to the "nix" command performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nix build -f. pkg1 ... pkg100 is 10x slower than a build with single expression

6 participants