Skip to content

lib/modules: Short-circuit unmatchedDefns when configs is empty#144022

Merged
infinisil merged 6 commits intoNixOS:masterfrom
hercules-ci:lib-modules-optimize-unmatchedDefns
Dec 7, 2021
Merged

lib/modules: Short-circuit unmatchedDefns when configs is empty#144022
infinisil merged 6 commits intoNixOS:masterfrom
hercules-ci:lib-modules-optimize-unmatchedDefns

Conversation

@roberth
Copy link
Member

@roberth roberth commented Oct 31, 2021

Motivation for this change

Improve performance.

By short-circuiting, we don't have to evaluate as much of the
options trees.

Allocation stats show -0.55% percent in heap size.
bench shows

Before:

benchmarking nix-instantiate -A nixosTests.specialisation -A nixosTests.installer -A nixosTests.nginx -A nixosTests.postgresql
time                 122.2 s    (118.7 s .. 124.9 s, ci 0.990)
                     1.000 R²   (1.000 R² .. 1.000 R², ci 0.990)
mean                 120.8 s    (119.2 s .. 121.8 s, ci 0.990)
std dev              1.170 s    (310.6 ms .. 1.601 s, ci 0.990)
variance introduced by outliers: 19% (moderately inflated)

After:

time                 121.9 s    (116.2 s .. 128.3 s, ci 0.990)
                     1.000 R²   (NaN R² .. 1.000 R², ci 0.990)
mean                 120.2 s    (118.7 s .. 121.4 s, ci 0.990)
std dev              1.340 s    (313.6 ms .. 1.656 s, ci 0.990)
variance introduced by outliers: 19% (moderately inflated)

suggesting a 1.6% performance improvement, although that number is still bogus, considering the possible range of each combined measurement. Maybe I should have picked a lower confidence interval. I was hoping it would produce more samples for a high ci. I might still try that.

Performance report for d6ebd53 https://github.com/NixOS/nixpkgs/pull/144022/checks?check_run_id=4062089224

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • 21.11 Release Notes (or backporting 21.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux. labels Oct 31, 2021
@roberth roberth force-pushed the lib-modules-optimize-unmatchedDefns branch from 1aeaf7a to 75d67de Compare October 31, 2021 20:30
@roberth roberth force-pushed the lib-modules-optimize-unmatchedDefns branch from 75d67de to d6ebd53 Compare October 31, 2021 21:29
@roberth
Copy link
Member Author

roberth commented Nov 3, 2021

@infinisil could you double-check?

lib/modules.nix Outdated
# Propagate all unmatched definitions from nested option sets
mapAttrs (n: v: v.unmatchedDefns) resultsByName
# Plus the definitions for the current prefix that don't have a matching option
// removeAttrs defnsByName' (attrNames matchedOptions);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After taking a closer look, I think the main savings come from defnsByName' not being evaluated when configs == [], meaning that config sections aren't evaluated when not needed.

Since this does make module more lazy to some degree it would be awesome if we could have a test case that shows that ensures that extra lazy behavior so we can see the impact and prevent future regressions of it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this for options now, but not for config yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a test case. The savings may come exclusively from generating fewer internal datastructures.

Doesn't seem to have been a problem actually, but now it won't
regress.
In hot code, the overhead (envs, applies) can matter.
Very confusing otherwise.
recursiveUpdate does not produce an attrset until it has evaluated
both its arguments to weak head normal form.

    nix-repl> lib.recursiveUpdate (throw "a") (throw "b")
    error: b

    nix-repl> lib.recursiveUpdate (throw "a") {}
    error: a
@roberth
Copy link
Member Author

roberth commented Nov 3, 2021

I've spotted some low hanging fruit that I've added here because it's simple enough. Will make separate PRs for other improvements though, because ofborg stats aren't comparable as it merges with (mutable) master before each run.

@roberth
Copy link
Member Author

roberth commented Nov 3, 2021

@infinisil That's all, as far as I'm concerned.

There's potential for more laziness, allowing mkOption to be replaced by bottom, by removing the count call and making unmatchedDefns tree-shaped, but I'm not going there with this PR. It seems rather too invasive because that data structure also affects types.
EDIT: coming to think of it, merging before the type merges should be fine as long as it happens on a "per-module" basis. (Scare quotes because that includes mkMerge items. Not sure what the term is for that) Still not going there with this PR though.

Copy link
Member

@infinisil infinisil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, looking good!

@infinisil infinisil merged commit ae0b7d6 into NixOS:master Dec 7, 2021
@andir
Copy link
Member

andir commented Dec 11, 2021

I haven't invested much time but apparently this broke the way extraArgs are passed to module. My morph deployments now don't get the name attribute passed to modules anymore and thus I am unable to extract the current nodes name in a module (without resorting to other module system parameters). It looks like @eadwu ran into the same issue but simply reverted it locally?

@roberth
Copy link
Member Author

roberth commented Dec 11, 2021

@andir Are you sure it isn't fixed by #148315?

I don't know about eadwu's #144022 (reference), but it doesn't seem to be part of any ref anymore.

@andir
Copy link
Member

andir commented Dec 11, 2021

@andir Are you sure it isn't fixed by #148315?

I don't know about eadwu's #144022 (reference), but it doesn't seem to be part of any ref anymore.

I think you are right. It must be that other PR. I somehow didn't find that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 0 This PR does not cause any packages to rebuild on Linux.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants