forked from lix-project/lix
* Don't create thunks for variable lookups (if possible). This
significantly reduces the number of values allocated (e.g. from 8.7m to 4.9m for the Bittorrent test).
This commit is contained in:
parent
0b305c534f
commit
2dc6d50941
src/libexpr
|
@ -316,6 +316,11 @@ Env & EvalState::allocEnv(unsigned int size)
|
||||||
nrEnvs++;
|
nrEnvs++;
|
||||||
nrValuesInEnvs += size;
|
nrValuesInEnvs += size;
|
||||||
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
||||||
|
|
||||||
|
/* Clear the values because maybeThunk() expects this. */
|
||||||
|
for (unsigned i = 0; i < size; ++i)
|
||||||
|
env->values[i] = 0;
|
||||||
|
|
||||||
return *env;
|
return *env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,12 +351,48 @@ void EvalState::mkAttrs(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long nrThunks = 0;
|
||||||
|
|
||||||
|
static inline void mkThunk(Value & v, Env & env, Expr * expr)
|
||||||
|
{
|
||||||
|
v.type = tThunk;
|
||||||
|
v.thunk.env = &env;
|
||||||
|
v.thunk.expr = expr;
|
||||||
|
nrThunks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::mkThunk_(Value & v, Expr * expr)
|
void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||||
{
|
{
|
||||||
mkThunk(v, baseEnv, expr);
|
mkThunk(v, baseEnv, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long nrAvoided = 0;
|
||||||
|
|
||||||
|
/* Create a thunk for the delayed computation of the given expression
|
||||||
|
in the given environment. But if the expression is a variable,
|
||||||
|
then look it up right away. This significantly reduces the number
|
||||||
|
of thunks allocated. */
|
||||||
|
Value * EvalState::maybeThunk(Env & env, Expr * expr)
|
||||||
|
{
|
||||||
|
ExprVar * var;
|
||||||
|
/* Ignore variables from `withs' because they can throw an
|
||||||
|
exception. */
|
||||||
|
if ((var = dynamic_cast<ExprVar *>(expr))) {
|
||||||
|
Value * v = lookupVar(&env, var->info);
|
||||||
|
/* The value might not be initialised in the environment yet.
|
||||||
|
In that case, ignore it. */
|
||||||
|
if (v) { nrAvoided++; return v; }
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * v = allocValue();
|
||||||
|
mkThunk(*v, env, expr);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::cloneAttrs(Value & src, Value & dst)
|
void EvalState::cloneAttrs(Value & src, Value & dst)
|
||||||
{
|
{
|
||||||
mkAttrs(dst);
|
mkAttrs(dst);
|
||||||
|
@ -478,8 +519,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
/* The recursive attributes are evaluated in the new
|
/* The recursive attributes are evaluated in the new
|
||||||
environment. */
|
environment. */
|
||||||
foreach (Attrs::iterator, i, attrs) {
|
foreach (Attrs::iterator, i, attrs) {
|
||||||
Value * vAttr = state.allocValue();
|
Value * vAttr = state.maybeThunk(env2, i->second.first);
|
||||||
mkThunk(*vAttr, env2, i->second.first);
|
|
||||||
env2.values[displ++] = vAttr;
|
env2.values[displ++] = vAttr;
|
||||||
v.attrs->push_back(nix::Attr(i->first, vAttr, &i->second.second));
|
v.attrs->push_back(nix::Attr(i->first, vAttr, &i->second.second));
|
||||||
}
|
}
|
||||||
|
@ -515,8 +555,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
else {
|
else {
|
||||||
foreach (Attrs::iterator, i, attrs) {
|
foreach (Attrs::iterator, i, attrs) {
|
||||||
nix::Attr & a = (*v.attrs)[i->first];
|
nix::Attr & a = (*v.attrs)[i->first];
|
||||||
a.value = state.allocValue();
|
a.value = state.maybeThunk(env, i->second.first);
|
||||||
mkThunk(*a.value, env, i->second.first);
|
|
||||||
a.pos = &i->second.second;
|
a.pos = &i->second.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,10 +579,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
/* The recursive attributes are evaluated in the new
|
/* The recursive attributes are evaluated in the new
|
||||||
environment. */
|
environment. */
|
||||||
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs) {
|
foreach (ExprAttrs::Attrs::iterator, i, attrs->attrs)
|
||||||
env2.values[displ] = state.allocValue();
|
env2.values[displ++] = state.maybeThunk(env2, i->second.first);
|
||||||
mkThunk(*env2.values[displ++], env2, i->second.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The inherited attributes, on the other hand, are evaluated in
|
/* The inherited attributes, on the other hand, are evaluated in
|
||||||
the original environment. */
|
the original environment. */
|
||||||
|
@ -558,7 +595,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.mkList(v, elems.size());
|
state.mkList(v, elems.size());
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||||
mkThunk(*(v.list.elems[n] = state.allocValue()), env, elems[n]);
|
v.list.elems[n] = state.maybeThunk(env, elems[n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -570,10 +607,15 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned long nrLookups = 0;
|
||||||
|
unsigned long nrLookupSize = 0;
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
nrLookups++;
|
||||||
Value v2;
|
Value v2;
|
||||||
state.evalAttrs(env, e, v2);
|
state.evalAttrs(env, e, v2);
|
||||||
|
nrLookupSize += v2.attrs->size();
|
||||||
Bindings::iterator i = v2.attrs->find(name);
|
Bindings::iterator i = v2.attrs->find(name);
|
||||||
if (i == v2.attrs->end())
|
if (i == v2.attrs->end())
|
||||||
throwEvalError("attribute `%1%' missing", name);
|
throwEvalError("attribute `%1%' missing", name);
|
||||||
|
@ -608,9 +650,7 @@ void ExprApp::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vFun;
|
Value vFun;
|
||||||
state.eval(env, e1, vFun);
|
state.eval(env, e1, vFun);
|
||||||
Value * vArg = state.allocValue();
|
state.callFunction(vFun, *state.maybeThunk(env, e2), v);
|
||||||
mkThunk(*vArg, env, e2);
|
|
||||||
state.callFunction(vFun, *vArg, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -685,8 +725,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||||
if (j == arg.attrs->end()) {
|
if (j == arg.attrs->end()) {
|
||||||
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
||||||
fun.lambda.fun->pos, i->name);
|
fun.lambda.fun->pos, i->name);
|
||||||
env2.values[displ] = allocValue();
|
env2.values[displ++] = maybeThunk(env2, i->def);
|
||||||
mkThunk(*env2.values[displ++], env2, i->def);
|
|
||||||
} else {
|
} else {
|
||||||
attrsUsed++;
|
attrsUsed++;
|
||||||
env2.values[displ++] = j->value;
|
env2.values[displ++] = j->value;
|
||||||
|
@ -1156,6 +1195,10 @@ void EvalState::printStats()
|
||||||
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
|
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
|
||||||
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
|
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
|
||||||
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
|
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
|
||||||
|
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||||
|
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||||
|
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||||
|
printMsg(v, format(" attr lookup size: %1%") % nrLookupSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -169,14 +169,6 @@ static inline void mkBool(Value & v, bool b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void mkThunk(Value & v, Env & env, Expr * expr)
|
|
||||||
{
|
|
||||||
v.type = tThunk;
|
|
||||||
v.thunk.env = &env;
|
|
||||||
v.thunk.expr = expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void mkApp(Value & v, Value & left, Value & right)
|
static inline void mkApp(Value & v, Value & left, Value & right)
|
||||||
{
|
{
|
||||||
v.type = tApp;
|
v.type = tApp;
|
||||||
|
@ -326,6 +318,8 @@ public:
|
||||||
void mkAttrs(Value & v);
|
void mkAttrs(Value & v);
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
void mkThunk_(Value & v, Expr * expr);
|
||||||
|
|
||||||
|
Value * maybeThunk(Env & env, Expr * expr);
|
||||||
|
|
||||||
void cloneAttrs(Value & src, Value & dst);
|
void cloneAttrs(Value & src, Value & dst);
|
||||||
|
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
|
|
|
@ -1070,7 +1070,7 @@ void EvalState::createBaseEnv()
|
||||||
/* Add a wrapper around the derivation primop that computes the
|
/* Add a wrapper around the derivation primop that computes the
|
||||||
`drvPath' and `outPath' attributes lazily. */
|
`drvPath' and `outPath' attributes lazily. */
|
||||||
string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }";
|
string s = "attrs: let res = derivationStrict attrs; in attrs // { drvPath = res.drvPath; outPath = res.outPath; type = \"derivation\"; }";
|
||||||
mkThunk(v, baseEnv, parseExprFromString(*this, s, "/"));
|
mkThunk_(v, parseExprFromString(*this, s, "/"));
|
||||||
addConstant("derivation", v);
|
addConstant("derivation", v);
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
|
|
Loading…
Reference in a new issue