From 3f796514b37a1e723a395fce8271428410e93f5f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 2 Jan 2024 12:39:16 +0100 Subject: [PATCH 1/2] Optimize empty list constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids a Value allocation for empty list constants. During a `nix search nixpkgs`, about 82% of all thunked lists are empty, so this removes about 3 million Value allocations. Performance comparison on `nix search github:NixOS/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870 --no-eval-cache`: maximum RSS: median = 3845432.0000 mean = 3845432.0000 stddev = 0.0000 min = 3845432.0000 max = 3845432.0000 [rejected?, p=0.00000, Δ=-70084.00000±0.00000] soft page faults: median = 965395.0000 mean = 965394.6667 stddev = 1.1181 min = 965392.0000 max = 965396.0000 [rejected?, p=0.00000, Δ=-17929.77778±38.59610] system CPU time: median = 1.8029 mean = 1.7702 stddev = 0.0621 min = 1.6749 max = 1.8417 [rejected, p=0.00064, Δ=-0.12873±0.09905] user CPU time: median = 14.1022 mean = 14.0633 stddev = 0.1869 min = 13.8118 max = 14.3190 [not rejected, p=0.03006, Δ=-0.18248±0.24928] elapsed time: median = 15.8205 mean = 15.8618 stddev = 0.2312 min = 15.5033 max = 16.1670 [not rejected, p=0.00558, Δ=-0.28963±0.29434] --- src/libexpr/eval.cc | 11 +++++++++++ src/libexpr/eval.hh | 3 +++ src/libexpr/nixexpr.hh | 1 + 3 files changed, 15 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 810843995..494b8338f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -554,6 +554,8 @@ EvalState::EvalState( static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); + vEmptyList.mkList(0); + /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { for (auto & i : _searchPath.elements) @@ -1384,6 +1386,15 @@ void ExprList::eval(EvalState & state, Env & env, Value & v) } +Value * ExprList::maybeThunk(EvalState & state, Env & env) +{ + if (elems.empty()) { + return &state.vEmptyList; + } + return Expr::maybeThunk(state, env); +} + + void ExprVar::eval(EvalState & state, Env & env, Value & v) { Value * v2 = state.lookupVar(&env, *this, false); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index da2d256db..bf85b50c8 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -305,6 +305,9 @@ public: return *errorBuilder; } + /* Empty list constant. */ + Value vEmptyList; + private: /* Cache for calls to addToStore(); maps source paths to the store diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 1e57fec7a..55e930758 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -299,6 +299,7 @@ struct ExprList : Expr std::vector elems; ExprList() { }; COMMON_METHODS + Value * maybeThunk(EvalState & state, Env & env) override; PosIdx getPos() const override { From 484881f3021856cd0d0c0cb42d4473b3c7ea0051 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jan 2024 10:23:27 +0100 Subject: [PATCH 2/2] Move empty list constant --- src/libexpr/eval.hh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index bf85b50c8..e2180f00d 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -218,6 +218,11 @@ public: Bindings emptyBindings; + /** + * Empty list constant. + */ + Value vEmptyList; + /** * The accessor for the root filesystem. */ @@ -305,9 +310,6 @@ public: return *errorBuilder; } - /* Empty list constant. */ - Value vEmptyList; - private: /* Cache for calls to addToStore(); maps source paths to the store