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
This commit is contained in:
+34
-35
@@ -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 =
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user