-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
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
pkgs: introduce package-list.nix
and trivialPackages
stage
#50643
Conversation
As a side note, to convert all of |
The idea here is to move as much of `all-packages.nix` to `package-list.nix` as humanly possible. Pros: - It improves metrics because `import package-list.nix` gets cached between stages. Meanwhile, doing the same with `import all-packages.nix` produces a negative impact because `all-packages.nix` is complicated and caching it adds pressure on GC. Meanwhile, `package-list.nix` is just a list of paths, which is cheap. - It allows for easy experimentation with alternatives to `callPackage` as `package-list.nix` is, by definition, a set of trivial packages. (This was the initial motivation for this change.) - It's homogeneous with NixOS' `module-list.nix`. - Eventually `package-list.nix` can be completely autogenerated. Cons: - Migration to `package-list.nix` does introduce some code churn. This also adds `maintainers/scripts/all-packages-to-package-list.sh` script that does the (very conservative, currently) conversion automagically.
Hint: if this conflicts with your change you can revert this commit, rebase your change on top, run the script yourself, commit the result, squash. It's magic.
ed488ce
to
1970134
Compare
Ping. I see thumbs down, but can I have an explanation why? I see no reason not to proceed in this way except for the churn. And the churn won't be too much of an issue if we migrate in tiny installments (i.e. if we agree to merge the infra from the first commit and start adding new packages there). Concerning package arguments, I think the better policy to doing what the current policy does is switching to using the following method: Now:
After:
|
I'm not even against making an RFC for this change, but I want to hear out some thoughts first. Disclaimer: Yes, I want this because it greatly simplifies the proper implementation of #12877, but even if you don't want use-flags functionality in Nixpkgs I see no reason not to implement this particular change. |
Re: re-importing: not sure if it would be better than mapping callPackage over the list in Future plans of modularising Nixpkgs seems to be an argument in favour of doing version selection in-package. Treewide version pinning changes might become more complicated, though. Unlike |
Please elaborate, I don't understand what you are trying to convey in that paragraph. "mapping callPackage over the list in all-packages.nix" what list?
I don't see why, you can pin in
Oh, yes, that bugs me too. |
re: re-importing: I expect that I am not sure that the extra caching will be noticeable — I would expect that looking up any attribute name in the resulting attrset should end up with the same outcome as evaluation of the current Re: pinning: replacing a single argument name in a package is a simpler operation that doing something about two related ones. |
This was tried before (ece61b7) but was reverted. In short, it's confusing if some packages are in |
I expect that `all-packages.nix` will stay in some form even if the current PR is adopted, so a full simplification won't happen.
For now, that's correct. Ideally, though, `all-packages.nix` should just become the "default version selector overlay". But that needs considerably more work than I managed to put in till now (I do have a branch that moves all option defaults from `all-packages.nix` to the package expressions already, though, but I'm not happy about it yet) as `all-packages.nix` has all kinds fun stuff ATM.
I am not sure that the extra caching will be noticeable — I would expect that looking up any attribute name in the resulting attrset should end up with the same outcome as evaluation of the current `all-packages.nix`; the `mapAttrs` will need to be evaluated enough to have the list of names.
Right, but that's exactly what I don't do here. The
```
packageList = lib.listToAttrs
(map (path: lib.nameValuePair (baseNameOf (toString path)) path) (import ../package-list.nix));
```
bit that gets cached doesn't `map` with `callPackage`. That's the whole point. `packageList` is an attrset of paths, which is very cheap. Meanwhile, `import all-packages.nix` produces an attrset of code thunks, which, as you rightfully note, is not cheap.
The effect of this is quite noticeable already (I still need to measure carefully, but I think it was about 10% in -qa time last time I checked), even before most of `all-packages.nix` is converted.
(And that whole `map list into attrset` construction could be made into a builtin to speed up the thing even more as it wastes allocations by constantly recycling `nameValuePair`s.)
Re: pinning: replacing a single argument name in a package is a simpler operation that doing something about two related ones.
Well, strictly speaking, there are two places now too: one for specifying the global default version, and one for overriding in `callPackage` block for each packages that wants a different one. No real change here. So, ideally, if a package wants a particular version it would specify it with the above default attrset trick instead of the `callPackage` block like now, if it does not, it simply receives a default from the "default version selector overlay" as usual.
|
In short, it's confusing if some packages are in `all-packages.nix` and some aren't.
That's a fair point for the transition period, but it stops being an issue when `all-packages.nix` becomes "default version selector overlay" as noted above.
And if you need to override an argument, you now need to modify two files. Saving a few lines didn't really justify this.
No, you don't, you just override the default with a default value in the package expression itself, see above.
|
The effect of this is quite noticeable already (I still need to measure carefully, but I think it was about 10% in -qa time last time I checked), even before most of `all-packages.nix` is converted.
Is there an import that doesn't get accessed at all? Because I don't see how an access wouldn't create an attrset of thunks.
|
Is there an import that doesn't get accessed at all? Because I don't see how an access wouldn't create an attrset of thunks.
`pkgs/top-level/default.nix`, basically:
```
allPackages = newArgs: import ./stage.nix ({
inherit lib nixpkgsFun;
} // newArgs);
boot = import ../stdenv/booter.nix { inherit lib allPackages; };
in boot stages
```
`pkgs/stdenv/booter.nix` then evaluates `allPackages` a bunch of times to bootstrap `stdenv`, each eval of that imports `stage.nix` and then evaluates the result as a function on that `... // newArgs` argument.
Here goes the important bit.
The body of the function defined in `stage.nix` imports `all-packages.nix`. Since `allPackages` gets evaluated multiple times, `stage.nix` function gets evaluated multiple times, which re-imports `all-packages.nix` multiple times (which is easy to check by adding a `trace` there).
But if you move that import of `all-packages.nix` from `stage.nix` to `default.nix` (kinda like I did with `packageList`) you will actually loose performance because nix will have to keep two copies of it in memory while applying arguments evaluating the package set: one for the initial cached version it can't GC because it's cached, and another one for the evaluation of the function `all-packages.nix` contains.
With `packageList` there's no such issue, it's not a function, it's an attrset of paths. And converting larger chunks of `all-packages.nix` to `package-list.nix` will produce larger difference in performance.
I do need to measure more stuff, though, like I have a feeling that adding some tactical `deepSeq` there might produce an even better result.
|
I've made a test which showed:
Also, I've added a third option, indexed all-package.nix. It is like
and measures how better would things be if "callPackage" string is offloaded until real need. So, the result for 40000 packages:
The test script:
|
Yeah, before I suspended this (temporary) I, too, decided to go with a simple attrset and only offload the `callPackage`. My motivation for the list route was conceptual simplicity, but going the list route is just too hard with all the weird discrepancies between attr and file names the current `./all-packages.nix` has, which adds to the complexity of the mapping function, which quickly reduces any efficiency gains to nil.
- I don't observe "metrics improved"
No changes for `nix-env -qa` metrics? On either variant?
|
no, in my simple test. I hope it tests the pure extract of your idea. |
Are there any updates on this pull request, please? |
Thank you for your contributions.
|
I marked this as stale due to inactivity. → More info |
a similar thing https://github.com/nixpkgs-architecture/simple-package-paths |
The idea here is to move as much of
all-packages.nix
topackage-list.nix
as humanly possible.Pros:
It improves metrics because
import package-list.nix
gets cached between stages. Meanwhile, doing the same withimport all-packages.nix
produces a negative impact becauseall-packages.nix
is complicated and caching it adds pressure on GC. Meanwhile,package-list.nix
is just a list of paths, which is cheap.It allows for easy experimentation with alternatives to
callPackage
aspackage-list.nix
is, by definition, a set of trivial packages. (This was the initial motivation for this change.)It's homogeneous with NixOS'
module-list.nix
.Eventually
package-list.nix
can be completely autogenerated.Cons:
package-list.nix
does introduce some code churn.This also adds
maintainers/scripts/all-packages-to-package-list.sh
script that does the (very conservative, currently) conversion automagically.Take you time merging and testing this, conflicts are easy to fix: just rebase dropping the last commit and rerun the script.
Yes, this is a massive noop change. :)
/cc @matthewbauer @Ericson2314 @vcunat @edolstra