Skip to content

Instantly share code, notes, and snippets.

@aakropotkin
Last active April 4, 2023 13:21
Show Gist options
  • Save aakropotkin/51f4602c2e70a83e29f33cc03f493036 to your computer and use it in GitHub Desktop.
Save aakropotkin/51f4602c2e70a83e29f33cc03f493036 to your computer and use it in GitHub Desktop.
Compare performance of foldl' and listToAttrs in nix

Table of Contents

  1. Case 1: foldl'
  2. Case 2: listToAttrs with getAttr
  3. Case 3: listToAttrs with attrs.${name}

Compare performance of listToAttrs, foldl', getAttr, and attrs.${name}.

Conclusion:

Use of foldl' for cases where listToAttrs could be used instead has a major performance difference. When processing the same attrset, foldl' was roughly 120 times slower!

Case 1: foldl'

let
  attrs = builtins.readDir /nix/store;
  rsl   = builtins.foldl' ( acc: name: acc // { ${name} = attrs.${name}; } ) {}
                          ( builtins.attrNames attrs );
in builtins.deepSeq rsl null

$ NIX_SHOW_STATS=1 nix eval -f ex1.nix;
{
  "cpuTime": 6.032771110534668,
  "envs": {
    "bytes": 1516736,
    "elements": 63198,
    "number": 63197
  },
  "gc": {
    "heapSize": 8137211904,
    "totalBytes": 7998121264
  },
  "list": {
    "bytes": 252816,
    "concats": 0,
    "elements": 31602
  },
  "nrAvoided": 4,
  "nrFunctionCalls": 63196,
  "nrLookups": 31602,
  "nrOpUpdateValuesCopied": 499232600,
  "nrOpUpdates": 31598,
  "nrPrimOpCalls": 4,
  "nrThunks": 31603,
  "sets": {
    "bytes": 7989746128,
    "elements": 499295932,
    "number": 63201
  },
  "sizes": {
    "Attr": 16,
    "Bindings": 16,
    "Env": 16,
    "Value": 24
  },
  "symbols": {
    "bytes": 1738428,
    "number": 31845
  },
  "values": {
    "bytes": 3036504,
    "number": 126521
  }
}

Case 2: listToAttrs with getAttr

let
  attrs = builtins.readDir /nix/store;
  proc  = name: { inherit name; value = builtins.getAttr name attrs; };
  rsl   = builtins.listToAttrs ( map proc ( builtins.attrNames attrs ) );
in builtins.deepSeq rsl null

$ NIX_SHOW_STATS=1 nix eval -f ex2.nix;
{
  "cpuTime": 0.048695001751184464,
  "envs": {
    "bytes": 758392,
    "elements": 31601,
    "number": 31599
  },
  "gc": {
    "heapSize": 402915328,
    "totalBytes": 10122112
  },
  "list": {
    "bytes": 505600,
    "concats": 0,
    "elements": 63200
  },
  "nrAvoided": 94799,
  "nrFunctionCalls": 31598,
  "nrLookups": 31602,
  "nrOpUpdateValuesCopied": 0,
  "nrOpUpdates": 0,
  "nrPrimOpCalls": 31603,
  "nrThunks": 31603,
  "sets": {
    "bytes": 2530128,
    "elements": 126528,
    "number": 31605
  },
  "sizes": {
    "Attr": 16,
    "Bindings": 16,
    "Env": 16,
    "Value": 24
  },
  "symbols": {
    "bytes": 1738429,
    "number": 31845
  },
  "values": {
    "bytes": 3036528,
    "number": 126522
  }
}

Case 3: listToAttrs with attrs.${name}

let
  attrs = builtins.readDir /nix/store;
  proc  = name: { inherit name; value = attrs.${name}; };
  rsl   = builtins.listToAttrs ( map proc ( builtins.attrNames attrs ) );
in builtins.deepSeq rsl null

$ NIX_SHOW_STATS=1 nix eval -f ex3.nix;
{
  "cpuTime": 0.042525000870227814,
  "envs": {
    "bytes": 758392,
    "elements": 31601,
    "number": 31599
  },
  "gc": {
    "heapSize": 402915328,
    "totalBytes": 10122112
  },
  "list": {
    "bytes": 505600,
    "concats": 0,
    "elements": 63200
  },
  "nrAvoided": 31603,
  "nrFunctionCalls": 31598,
  "nrLookups": 31602,
  "nrOpUpdateValuesCopied": 0,
  "nrOpUpdates": 0,
  "nrPrimOpCalls": 5,
  "nrThunks": 31603,
  "sets": {
    "bytes": 2530128,
    "elements": 126528,
    "number": 31605
  },
  "sizes": {
    "Attr": 16,
    "Bindings": 16,
    "Env": 16,
    "Value": 24
  },
  "symbols": {
    "bytes": 1738429,
    "number": 31845
  },
  "values": {
    "bytes": 3036528,
    "number": 126522
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment