From 8b99b75698c69424f5429f5fc01c50f98155d333 Mon Sep 17 00:00:00 2001 From: skye Date: Sun, 8 Mar 2026 14:45:17 -0400 Subject: [PATCH] libexpr: Push Values onto vectors instead of default constructing ahead of time Rather than creating fixed size vectors of default constructed Values before assigning to those elements, reserve the desired capacity and then push created values onto the vector. This avoids default constructing any Values. Part of fixing #744 Change-Id: I36eff4275a893b181eaf3ce145b1ee446a6a6964 --- lix/libexpr/eval-expr.cc | 13 +++++++------ lix/libexpr/primops.cc | 39 +++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/lix/libexpr/eval-expr.cc b/lix/libexpr/eval-expr.cc index 298647c7a..e1bc72307 100644 --- a/lix/libexpr/eval-expr.cc +++ b/lix/libexpr/eval-expr.cc @@ -429,12 +429,12 @@ Value ExprConcatStrings::eval(EvalState & state, Env & env) }; // List of returned strings. References to these Values must NOT be persisted. - SmallTemporaryValueVector values(es.size()); - Value * vTmpP = values.data(); + SmallTemporaryValueVector values; + values.reserve(es.size()); for (auto & [i_pos, i] : es) { - Value & vTmp = *vTmpP++; - vTmp = i->eval(state, env); + values.push_back(i->eval(state, env)); + Value & vTmp = values.back(); /* If the first element is a path, then the result will also be a path, we don't copy anything (yet - that's done later, @@ -715,9 +715,10 @@ Value ExprCall::eval(EvalState & state, Env & env) // 5: under 10 // This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 // total. - SmallValueVector<4> vArgs(args.size()); + SmallValueVector<4> vArgs; + vArgs.reserve(args.size()); for (size_t i = 0; i < args.size(); ++i) { - vArgs[i] = args[i]->maybeThunk(state, env); + vArgs.push_back(args[i]->maybeThunk(state, env)); } return state.callFunction(vFun, vArgs, pos); diff --git a/lix/libexpr/primops.cc b/lix/libexpr/primops.cc index 690ca89bb..d3fd4a243 100644 --- a/lix/libexpr/primops.cc +++ b/lix/libexpr/primops.cc @@ -2109,8 +2109,8 @@ static void prim_catAttrs(EvalState & state, Value * * args, Value & v) auto attrName = state.ctx.symbols.create(state.forceStringNoCtx(*args[0], noPos, "while evaluating the first argument passed to builtins.catAttrs")); state.forceList(*args[1], noPos, "while evaluating the second argument passed to builtins.catAttrs"); - SmallValueVector res(args[1]->listSize()); - size_t found = 0; + SmallValueVector res; + res.reserve(args[1]->listSize()); for (auto & v2 : args[1]->listItems()) { state.forceAttrs( @@ -2120,15 +2120,13 @@ static void prim_catAttrs(EvalState & state, Value * * args, Value & v) ); auto i = v2.attrs()->get(attrName); if (i) { - res[found++] = i->value; + res.push_back(i->value); } } - auto result = state.ctx.mem.newList(found); + auto result = state.ctx.mem.newList(res.size()); + std::copy(res.cbegin(), res.cend(), result->elems); v = {NewValueAs::list, result}; - for (size_t n = 0; n < found; ++n) { - result->elems[n] = res[n]; - } } static void prim_functionArgs(EvalState & state, Value * * args, Value & v) @@ -2310,26 +2308,30 @@ static void prim_filter(EvalState & state, Value * * args, Value & v) state.forceFunction(*args[0], noPos, "while evaluating the first argument passed to builtins.filter"); auto len = args[1]->listSize(); - SmallValueVector vs(len); - size_t k = 0; + SmallValueVector vs; + vs.reserve(len); bool same = true; for (size_t n = 0; n < len; ++n) { Value res = state.callFunction(*args[0], args[1]->listElems()[n], noPos); - if (state.forceBool(res, noPos, "while evaluating the return value of the filtering function passed to builtins.filter")) - vs[k++] = args[1]->listElems()[n]; - else + if (state.forceBool( + res, + noPos, + "while evaluating the return value of the filtering function passed to builtins.filter" + )) + { + vs.push_back(args[1]->listElems()[n]); + } else { same = false; + } } if (same) v = *args[1]; else { - auto result = state.ctx.mem.newList(k); + auto result = state.ctx.mem.newList(vs.size()); v = {NewValueAs::list, result}; - for (unsigned int n = 0; n < k; ++n) { - result->elems[n] = vs[n]; - } + std::copy(vs.cbegin(), vs.cend(), result->elems); } } @@ -2570,12 +2572,13 @@ static void prim_concatMap(EvalState & state, Value * * args, Value & v) auto nrLists = args[1]->listSize(); // List of returned lists before concatenation. References to these Values must NOT be persisted. - SmallTemporaryValueVector lists(nrLists); + SmallTemporaryValueVector lists; + lists.reserve(nrLists); size_t len = 0; for (size_t n = 0; n < nrLists; ++n) { Value & vElem = args[1]->listElems()[n]; - lists[n] = state.callFunction(*args[0], vElem, noPos); + lists.push_back(state.callFunction(*args[0], vElem, noPos)); state.forceList(lists[n], noPos, "while evaluating the return value of the function passed to builtins.concatMap"); len += lists[n].listSize(); }