From 6ae8efaf5f5c0e8ee87932cd5f44c1215964b863 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sat, 4 Apr 2026 14:00:10 +0200 Subject: [PATCH] splice: replace listToAttrs+map with mapAttrs listToAttrs forces every list element to extract `.name`, calling merge eagerly for the entire union of attribute names (~60k). mapAttrs already knows the keys; the lambda is only invoked when a key is selected, letting the 99.7% unforced ones stay as untouched thunks. NIX_SHOW_STATS delta on pkgsCross.aarch64-multiplatform.hello.drvPath: nrFunctionCalls: 567,512 -> 507,404 (-10.6%) nrThunks: 613,138 -> 552,356 (-9.9%) sets.number: 158,787 -> 98,339 (-38.1%) list.elements: 295,085 -> 174,189 (-41.0%) gc.totalBytes: 117.5M -> 111.7M (-4.9%) Repro: NIX_SHOW_STATS=1 nix-instantiate --eval \ -E '(import ./. {}).pkgsCross.aarch64-multiplatform.hello.drvPath' \ 2>stats.json >/dev/null jq . stats.json --- pkgs/top-level/splice.nix | 69 +++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/pkgs/top-level/splice.nix b/pkgs/top-level/splice.nix index 2c6f060f23e1..5468d23c377d 100644 --- a/pkgs/top-level/splice.nix +++ b/pkgs/top-level/splice.nix @@ -31,43 +31,42 @@ let # The same pkgs sets one probably intends // inputs.buildHost // inputs.hostTarget; - merge = name: { - inherit name; - value = - let - defaultValue = mash.${name}; - # `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity. - value' = mapCrossIndex (x: x.${name} or { }) inputs; + # perf: mapAttrs defers merge calls until a key is selected, avoiding + # ~60k eager closures that listToAttrs+map would create. + merge = + name: defaultValue: + let + # `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity. + value' = mapCrossIndex (x: x.${name} or { }) inputs; - augmentedValue = defaultValue // { - __spliced = lib.filterAttrs (k: v: inputs.${k} ? ${name}) value'; - }; - # Get the set of outputs of a derivation. If one derivation fails to - # evaluate we don't want to diverge the entire splice, so we fall back - # on {} - tryGetOutputs = - value0: - let - inherit (builtins.tryEval value0) success value; - in - getOutputs (lib.optionalAttrs success value); - getOutputs = - value: lib.genAttrs (value.outputs or (lib.optional (value ? out) "out")) (output: value.${output}); - in - # The derivation along with its outputs, which we recur - # on to splice them together. - if lib.isDerivation defaultValue then - augmentedValue - // spliceReal (mapCrossIndex tryGetOutputs value' // { hostTarget = getOutputs value'.hostTarget; }) - else if lib.isAttrs defaultValue then - spliceReal value' - else - # Don't be fancy about non-derivations. But we could have used used - # `__functor__` for functions instead. - defaultValue; - }; + augmentedValue = defaultValue // { + __spliced = lib.filterAttrs (k: v: inputs.${k} ? ${name}) value'; + }; + # Get the set of outputs of a derivation. If one derivation fails to + # evaluate we don't want to diverge the entire splice, so we fall back + # on {} + tryGetOutputs = + value0: + let + inherit (builtins.tryEval value0) success value; + in + getOutputs (lib.optionalAttrs success value); + getOutputs = + value: lib.genAttrs (value.outputs or (lib.optional (value ? out) "out")) (output: value.${output}); + in + # The derivation along with its outputs, which we recur + # on to splice them together. + if lib.isDerivation defaultValue then + augmentedValue + // spliceReal (mapCrossIndex tryGetOutputs value' // { hostTarget = getOutputs value'.hostTarget; }) + else if lib.isAttrs defaultValue then + spliceReal value' + else + # Don't be fancy about non-derivations. But we could have used used + # `__functor__` for functions instead. + defaultValue; in - lib.listToAttrs (map merge (lib.attrNames mash)); + builtins.mapAttrs merge mash; splicePackages = {