-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf: Remove the quadratic behavior in haskellPackages.ghcWithPackages #194391
perf: Remove the quadratic behavior in haskellPackages.ghcWithPackages #194391
Conversation
@ofborg build tests.haskell.shellFor |
Damned, thank you @sternenseemann, it fails on |
|
Could this function be put in lib so we can convert other users as well? |
@FRidh It could yes. In which |
If the behaviour is the same it could replace the original function. Or is there any reason for not doing that? |
Will debugging, the new function is missing some values: I'm trying to understand. That's weird because it is able to build my packages, so there may be a weird corner case I did not anticpiated. @FRidh I'm not sure the new function is as general as the old one. Because the old was able to return anything which is a 1d0
< /nix/store/014dicp6v8l44jwx0qiadmrk23j4xyk3-hspec-core-2.8.5
3,5d1
< /nix/store/0hgxgp65g0mg5mw58hdikhj0zzwwx7ni-hspec-discover-2.8.5
< /nix/store/1nv8b47dm0z8xf55jfzpmzsrz9fhkyjs-ansi-terminal-0.11.3
< /nix/store/1v197gcfjm47549wadipcbkbv2cmbiv3-profunctors-5.6.2
8,10d3
< /nix/store/3rmmdpg12m6ryrjdpwspyrcbhpwjzq51-StateVar-1.2.2
< /nix/store/4q8w5bsn9phpkz3wbhhxpci1lgjbhjf6-tf-random-0.5
< /nix/store/4ydclrfs2kxbq9195n7hw1qm8nbmnssy-hspec-expectations-0.8.2
12,16d4
< /nix/store/64xbi7926z9sv977p3gv421dnfb50xhi-strict-0.4.0.1
< /nix/store/6dx6pwy5pqx08b72filzbb3b0k5zlyj3-call-stack-0.4.0
< /nix/store/6i2fvi1akhfbm006dm3fgvf9xg2p9cw8-contravariant-1.5.5
< /nix/store/6qmqark6jxbglzbjwmyvn8l25a8ibgzm-quickcheck-io-0.2.0
< /nix/store/6vicys7f43ikc0ych1hmaln7702bbrxy-bifunctors-5.5.13
18d5
< /nix/store/846xg53r9fivzkdmpmr5yh6vkfy196f1-transformers-base-0.4.6
20,23d6
< /nix/store/8rwafhh7nrzifzb2m58rm7naxkarpza5-extensible-exceptions-0.1.1.4
< /nix/store/8vbs4n20qbh95m22sixy279k52cyzcj5-these-1.1.1.1
< /nix/store/99sr0597428hb7ib0b13ijw243agkngn-scientific-0.3.7.0
< /nix/store/bhwzkqg404gnlj70cjgjqcphbqxwfs0p-colour-2.3.6
26,27d8
< /nix/store/cq2i4wjw061sgn2qhzcgf3hky6mw991b-setenv-0.1.1.3
< /nix/store/dfv4ai9hi2f9avlammpf1qyhhslhmjxb-assoc-1.0.2
29,30d9
< /nix/store/fh0dc4ndn60b475hb1l36kpym5i7yhkk-regex-tdfa-1.3.2
< /nix/store/fnh9rwax3x0r8qjdh6jiv5yyy44ignbg-splitmix-0.1.0.4
33,34d11
< /nix/store/gdarb3wg3y2wi824wbnl2hvsc3v7np65-clock-0.8.3
< /nix/store/h9z3amqgmrpdlrc8bmcms6mf3nqy8dhj-old-locale-1.0.0.7
37d13
< /nix/store/hzg99hggqw6g48zlfwwqa6mrkhp6jawf-OneTuple-0.3.1
39,40d14
< /nix/store/i4rylsmkbkgzvffkvpbgdppacbf8wz7v-QuickCheck-2.14.2
< /nix/store/ipq5z3xa1d84ica0qfn15z96r9hv9xmh-xml-1.3.14
42d15
< /nix/store/jsxhrq9yjfq8s84xdrdad8zhrhzkqpqn-th-abstraction-0.4.5.0
44,45d16
< /nix/store/kgi26v8anani7ja7svrqj94dn1hgyri0-kan-extensions-5.2.5
< /nix/store/kh71s1yn68wib6kzm8m3r24v222ymabj-integer-logarithms-1.0.3.1
47,48d17
< /nix/store/l89yqkcjssmjwiggj84znl2ac8mkaadv-regex-posix-0.96.0.1
< /nix/store/lq8ilf068imv54np2l1s4zh91n1vkgv6-parallel-3.2.2.0
50d18
< /nix/store/mvfvj06vpiyjv4g25i4p490kjnfr4jpg-free-5.1.9
54d21
< /nix/store/p8cr9aav175i6lqicjffjjva58zkcyi0-regex-base-0.94.0.2
56d22
< /nix/store/w4bia9by4srqb0zz9zn01d64hawnhrd3-invariant-0.5.6
58,63d23
< /nix/store/wasm9bckfrxm9qgwfng0xvpc3nlikfxi-primitive-0.7.3.0
< /nix/store/wqxdrq2bi7wzjh561gl94k2bnik0v68z-indexed-traversable-instances-0.1.1.1
< /nix/store/yhpsk4bxql6dfnc8m86wd29y237in2zm-binary-orphans-1.0.3
< /nix/store/yp5fr91qkgh0vh3bvkgfgzkzrxiksq7g-ansi-wl-pprint-0.6.9
< /nix/store/z1xnays4jk1c09b70728rw3hpnwzsjqq-hostname-1.0
< /nix/store/z653l9xrs9hnv16ymiaa3naj5q6xcjqv-pretty-terminal-0.1.0.0
65d24
< /nix/store/zgqkpq7j5yrvnyjh09988i2iy42mahbp-comonad-5.0.8 |
Haha, I'm just stupid. Simple mistake when copy pasting (I've refactored the It is a bit slower, |
6bc2c86
to
74455b6
Compare
74455b6
to
aa5454e
Compare
@ofborg build tests.haskell.shellFor |
The tests should now pass (at least they pass locally). As suggested by @FRidh, I replaced the Looks like the behavior should be highly similar, except for the ordering of the results. There are not much usage of this function:
A quick look makes me think that we may be safe to do this change on the complete nixpkgs. However it may trigger a massive rebuild, so perhaps it should be merged to another branch. What do you think? |
Yeah, as-is, this now needs to target staging due to the mass rebuilds |
@guibou Please add a changelog entry for this to the release notes for 21.11, since it is a breaking change (which is probably not too worrysome, since probably very few downstream users will be using it without having an
|
@sternenseemann documentation added (note, I changed the notes for 22.11 instead of 21.11 as you suggested. Please tell me if you were really meaning 21.11). Thank you @teto who also told me to regenerate the db thing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please do update your commit message to conform to CONTRIBUTING.md, i.e. it should probably start with lib.closePropagation:
…gation The code of `lib.closePropagation` was internally using a recursion on the dependencies and returns all the derivation directly or indirectly referenced by buildInputs. `lib.closeProgation` is implemented in pure nix and uses an unique function for list which is quadratic and does "true" equality, which needs deep set comparison. Instead, we use the `builtins.genericClosure` which is implemented as a builtin and uses a more efficient sorting feature. Note that `genericClosure` needs a `key` to discriminate the values, we used the `outPath` which is unique and orderable. On benchmarks, it performs up to 15x time faster on a benchmark related to haskellPackages.ghcWithPackages.
ef5663c
to
a2cd604
Compare
@sternenseemann done. |
So, this breaks my dev shell, and I'm not sure how to fix it. { pkgs ? import <nixpkgs> {},
ghc ? pkgs.haskell.packages.ghc924,
llvm ? pkgs.llvmPackages_13.llvm,
}:
with pkgs;
let
ghc' = ghc.ghcWithPackages(ps: with ps; [
haskell-language-server cabal-install
profiterole profiteur
ghc
]);
in
pkgs.stdenv.mkDerivation {
name = "cosmothought";
nativeBuildInputs = [pkg-config pcre];
buildInputs = [
zlib icu ncurses.dev llvm icu racket pcre haskellPackages.alex haskellPackages.happy
bzip2 ghc'
];
shellHook = "eval $(egrep ^export ${ghc'}/bin/ghc)";
} The last few lines of the trace are: ❯ nix-shell --show-trace
error: attribute 'outPath' missing
at /nix/store/r2v7sig14g93wwf6ljr0jb0q1v2d1ngh-nixpkgs/nixpkgs/lib/deprecated.nix:171:15:
170| startSet = builtins.map (x: {
171| key = x.outPath;
| ^
172| val = x;
… while evaluating 'closePropagationFast'
at /nix/store/r2v7sig14g93wwf6ljr0jb0q1v2d1ngh-nixpkgs/nixpkgs/lib/deprecated.nix:168:26:
167| # See https://github.com/NixOS/nixpkgs/pull/194391 for details.
168| closePropagationFast = list:
| ^
169| builtins.map (x: x.val) (builtins.genericClosure {
… from call site
at /nix/store/r2v7sig14g93wwf6ljr0jb0q1v2d1ngh-nixpkgs/nixpkgs/pkgs/development/haskell-modules/with-packages-wrapper.nix:57:57:
56| packageCfgDir = "${libDir}/package.conf.d";
57| paths = lib.filter (x: x ? isHaskellLibrary) (lib.closePropagation packages);
| ^
58| hasLibraries = lib.any (x: x.isHaskellLibrary) paths;
… while evaluating anonymous lambda
at /nix/store/r2v7sig14g93wwf6ljr0jb0q1v2d1ngh-nixpkgs/nixpkgs/pkgs/development/haskell-modules/with-packages-wrapper.nix:17:1:
16| # (hpkgs: [ hpkgs.mtl hpkgs.lens ])
17| selectPackages:
| ^
18|
… from call site
at /home/spacekitteh/code/cosmothought/shell.nix:11:10:
10| let
11| ghc' = ghc.ghcWithPackages(ps: with ps; [
| ^ |
|
@sternenseemann Ok, removing the |
|
Ah. So what would it have been bringing in before this patch? |
Nothing, as it would have been filtered out by this line in
|
Description of changes
This code was using
lib.closePropagation
which internally do a recursion on the dependencies of ghcWithPackages and returns all the haskell dependencies.lib.closeProgation
is implemented in pure nix and uses an unique function for list which is quadratic and does "true" equality, which needs deep set comparison.Things done
Instead, we use the
builtins.genericClosure
which is implemented as a builtin and uses a more efficient sorting feature.Note that
genericClosure
needs akey
to discriminate the values, we used theoutPath
which is unique and orderable.On benchmarks, it performs up to 15x time faster.
sandbox = true
set innix.conf
? (See Nix manual)nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD"
. Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/
)nixos/doc/manual/md-to-db.sh
to update generated release notesTesting / Benchmark
I've tested this implementation on a large haskell codebase where I'm using nix as a build system. I have something like 250 calls to
ghcWithPackages
. When building everything,nix build --dry-run
was tacking up to20s
, now it runs in1.5s
.In order to compare, I've created the following benchmark, drop this file (name it
fiz.nix
) at the root of nixpkgsThen I'm benchmarking with
time nix build -f fiz.nix --dry-run
Without this change:
18.6s
With this change:
1.2s
Note that nix will still announce that it will fetch the same numbers of paths, but before that patch, it generates
94
ghc-xxx-with-packages.drv
and with this patch it generates147
distinct derivations. I don't understand why. I suppose that the ordering of the results is changing. I was however able to confirm that it behaves the same (by building my 250 packages and running their test suite).This change should not (as far as I'm aware) trigger a mass rebuild because it only impacts shell / dynamic environments.