diff options
author | pennae <github@quasiparticle.net> | 2022-03-08 15:26:17 +0100 |
---|---|---|
committer | pennae <github@quasiparticle.net> | 2022-04-21 21:56:34 +0200 |
commit | 8168a4cf4a4ecadd6868ea285cdb5729eb3e81bc (patch) | |
tree | 95c79ff7e5f1f907394493bfc4a4c74c45242aad /src/libexpr/attr-set.hh | |
parent | 8775be33931ec3b1cad97035ff3d5370a97178a1 (diff) |
shrink Attr by 8 bytes on 64bit machines
with position and symbol tables in place we can now shrink Attr by a full
pointer with some simple field reordering. since Attr is a very hot struct this
has substantial impact on memory use, decreasing GC allocations and heap size by
10-15% each. we also get a ~15% performance improvement due to reduced GC
loading.
pure parsing has taken a hit over the branch base because positions are now
slightly more expensive to create, but overall we get a noticeable improvement.
before (on memory-friendliness):
Benchmark 1: nix search --no-eval-cache --offline ../nixpkgs hello
Time (mean ± σ): 6.960 s ± 0.028 s [User: 5.832 s, System: 0.897 s]
Range (min … max): 6.886 s … 7.005 s 20 runs
Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix
Time (mean ± σ): 328.1 ms ± 1.7 ms [User: 295.8 ms, System: 32.2 ms]
Range (min … max): 324.9 ms … 331.2 ms 20 runs
Benchmark 3: nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
Time (mean ± σ): 2.688 s ± 0.029 s [User: 2.365 s, System: 0.238 s]
Range (min … max): 2.642 s … 2.742 s 20 runs
after:
Benchmark 1: nix search --no-eval-cache --offline ../nixpkgs hello
Time (mean ± σ): 6.902 s ± 0.039 s [User: 5.844 s, System: 0.783 s]
Range (min … max): 6.820 s … 6.956 s 20 runs
Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix
Time (mean ± σ): 330.7 ms ± 2.2 ms [User: 300.6 ms, System: 30.0 ms]
Range (min … max): 327.5 ms … 334.5 ms 20 runs
Benchmark 3: nix eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'
Time (mean ± σ): 2.330 s ± 0.027 s [User: 2.040 s, System: 0.234 s]
Range (min … max): 2.272 s … 2.383 s 20 runs
Diffstat (limited to 'src/libexpr/attr-set.hh')
-rw-r--r-- | src/libexpr/attr-set.hh | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index c42aba0f6..3bf23eeb2 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -15,11 +15,15 @@ struct Value; /* Map one attribute name to its value. */ struct Attr { + /* the placement of `name` and `pos` in this struct is important. + both of them are uint32 wrappers, they are next to each other + to make sure that Attr has no padding on 64 bit machines. that + way we keep Attr size at two words with no wasted space. */ SymbolIdx name; - Value * value; PosIdx pos; + Value * value; Attr(SymbolIdx name, Value * value, PosIdx pos = noPos) - : name(name), value(value), pos(pos) { }; + : name(name), pos(pos), value(value) { }; Attr() { }; bool operator < (const Attr & a) const { @@ -27,6 +31,11 @@ struct Attr } }; +static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *), + "performance of the evaluator is highly sensitive to the size of Attr. " + "avoid introducing any padding into Attr if at all possible, and do not " + "introduce new fields that need not be present for almost every instance."); + /* Bindings contains all the attributes of an attribute set. It is defined by its size and its capacity, the capacity being the number of Attr elements allocated after this structure, while the size corresponds to |