Commit a3b13a82 authored by Jörg Thalheim's avatar Jörg Thalheim
Browse files

lib/modules: short-circuit mergeDefinitions for plain singletons

The vast majority of option definitions reaching mergeDefinitions are
singletons whose value carries no _type wrapper (mkIf/mkMerge/
mkOverride/mkOrder). For those, the dischargeProperties ->
filterOverrides' -> sortProperties pipeline is a no-op yet still costs
~125k function calls and ~70k thunks on a minimal NixOS system eval.

Detect this case up front and reuse the input list as
defsFinal'.values, keeping the addErrorContext around the value probe
so error traces still point at the defining file. highestPrio is
defaultOverridePriority by construction.

A single-fold filterOverrides' was prototyped but allocates an attrset
per definition and regressed gc.totalBytes; with the fast path in
place that function is no longer hot enough to matter.

NIX_SHOW_STATS, minimal `nix-instantiate ./nixos -A system`:
  nrFunctionCalls  5490285 -> 5365172  (-125113)
  nrThunks         8182569 -> 8112150   (-70419)
  gc.totalBytes     640.0M -> 635.2M     (-4.8M)
parent 968bc288
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -1223,6 +1223,29 @@ let
          else
            defsFiltered.values;
      in
      # Fast path: the overwhelming majority of options have exactly one
      # definition whose value carries no property wrapper
      # (mkIf/mkMerge/mkOverride/mkOrder/definition). In that case the
      # discharge/filter/sort pipeline above is a no-op but still allocates
      # several intermediate lists and closures. Detect it up front and hand
      # the original singleton straight to the type merge. The let-bindings
      # above are lazy and thus never forced on this branch.
      if
        length defs == 1
        && (
          let
            d = head defs;
          in
          addErrorContext "while evaluating definitions from `${d.file}':" (
            !(isAttrs d.value && d.value ? _type)
          )
        )
      then
        {
          values = defs;
          highestPrio = defaultOverridePriority;
        }
      else
        {
          values = defsSorted;
          inherit (defsFiltered) highestPrio;