* 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:
Eelco Dolstra 2010-10-24 14:20:02 +00:00
parent 0b305c534f
commit 2dc6d50941
3 changed files with 60 additions and 23 deletions

View file

@ -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);
} }

View file

@ -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;
@ -325,6 +317,8 @@ public:
void mkList(Value & v, unsigned int length); void mkList(Value & v, unsigned int length);
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);

View file

@ -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