* Store lists as lists of pointers to values rather than as lists of

values.  This improves sharing and gives another speed up.
  Evaluation of the NixOS system attribute is now almost 7 times
  faster than the old evaluator.
This commit is contained in:
Eelco Dolstra 2010-04-15 00:37:36 +00:00
parent e41b5828db
commit 04c4bd3624
7 changed files with 41 additions and 34 deletions

View file

@ -61,7 +61,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
if (attrIndex >= v.list.length) if (attrIndex >= v.list.length)
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath); throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
v = v.list.elems[attrIndex]; v = *v.list.elems[attrIndex];
} }
} }

View file

@ -113,6 +113,7 @@ void run(Strings args)
doTest(state, "with { x = 1; }; let inherit x; y = x; in y"); doTest(state, "with { x = 1; }; let inherit x; y = x; in y");
doTest(state, "builtins.toXML 123"); doTest(state, "builtins.toXML 123");
doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }"); doTest(state, "builtins.toXML { a.b = \"x\" + \"y\"; c = [ 1 2 ] ++ [ 3 4 ]; }");
doTest(state, "builtins.attrNames { x = 1; y = 2; }");
state.printStats(); state.printStats();
} }

View file

@ -50,7 +50,7 @@ std::ostream & operator << (std::ostream & str, Value & v)
case tList: case tList:
str << "[ "; str << "[ ";
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.list.length; ++n)
str << v.list.elems[n] << " "; str << *v.list.elems[n] << " ";
str << "]"; str << "]";
break; break;
case tThunk: case tThunk:
@ -102,7 +102,7 @@ EvalState::EvalState()
, baseEnvDispl(0) , baseEnvDispl(0)
, staticBaseEnv(false, 0) , staticBaseEnv(false, 0)
{ {
nrEnvs = nrValuesInEnvs = nrValuesInLists = nrValues = 0; nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrEvaluated = recursionDepth = maxRecursionDepth = 0; nrEvaluated = recursionDepth = maxRecursionDepth = 0;
deepestStack = (char *) -1; deepestStack = (char *) -1;
@ -251,6 +251,7 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var)
Value * EvalState::allocValues(unsigned int count) Value * EvalState::allocValues(unsigned int count)
{ {
nrValues += count;
return new Value[count]; // !!! check destructor return new Value[count]; // !!! check destructor
} }
@ -268,8 +269,8 @@ void EvalState::mkList(Value & v, unsigned int length)
{ {
v.type = tList; v.type = tList;
v.list.length = length; v.list.length = length;
v.list.elems = allocValues(length); v.list.elems = new Value *[length];
nrValuesInLists += length; nrListElems += length;
} }
@ -461,8 +462,11 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v) 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) Value * vs = state.allocValues(v.list.length);
mkThunk(v.list.elems[n], env, elems[n]); for (unsigned int n = 0; n < v.list.length; ++n) {
v.list.elems[n] = &vs[n];
mkThunk(vs[n], env, elems[n]);
}
} }
@ -543,7 +547,6 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
primOp->primOp.fun(*this, vArgs, v); primOp->primOp.fun(*this, vArgs, v);
} else { } else {
Value * v2 = allocValues(2); Value * v2 = allocValues(2);
nrValues += 2;
v2[0] = fun; v2[0] = fun;
v2[1] = arg; v2[1] = arg;
v.type = tPrimOpApp; v.type = tPrimOpApp;
@ -734,8 +737,6 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
Value v2; state.eval(env, e2, v2); Value v2; state.eval(env, e2, v2);
state.forceList(v2); state.forceList(v2);
state.mkList(v, v1.list.length + v2.list.length); state.mkList(v, v1.list.length + v2.list.length);
/* !!! This loses sharing with the original lists. We could use a
tCopy node, but that would use more memory. */
for (unsigned int n = 0; n < v1.list.length; ++n) for (unsigned int n = 0; n < v1.list.length; ++n)
v.list.elems[n] = v1.list.elems[n]; v.list.elems[n] = v1.list.elems[n];
for (unsigned int n = 0; n < v2.list.length; ++n) for (unsigned int n = 0; n < v2.list.length; ++n)
@ -810,7 +811,7 @@ void EvalState::strictForceValue(Value & v)
else if (v.type == tList) { else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.list.length; ++n)
strictForceValue(v.list.elems[n]); strictForceValue(*v.list.elems[n]);
} }
} }
@ -951,11 +952,11 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (v.type == tList) { if (v.type == tList) {
string result; string result;
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.list.length; ++n) {
result += coerceToString(v.list.elems[n], result += coerceToString(*v.list.elems[n],
context, coerceMore, copyToStore); context, coerceMore, copyToStore);
if (n < v.list.length - 1 if (n < v.list.length - 1
/* !!! not quite correct */ /* !!! not quite correct */
&& (v.list.elems[n].type != tList || v.list.elems[n].list.length != 0)) && (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
result += " "; result += " ";
} }
return result; return result;
@ -1009,14 +1010,14 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tList: case tList:
if (v2.type != tList || v1.list.length != v2.list.length) return false; if (v2.type != tList || v1.list.length != v2.list.length) return false;
for (unsigned int n = 0; n < v1.list.length; ++n) for (unsigned int n = 0; n < v1.list.length; ++n)
if (!eqValues(v1.list.elems[n], v2.list.elems[n])) return false; if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
return true; return true;
case tAttrs: { case tAttrs: {
if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false; if (v2.type != tAttrs || v1.attrs->size() != v2.attrs->size()) return false;
Bindings::iterator i, j; Bindings::iterator i, j;
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
if (!eqValues(i->second, j->second)) return false; if (i->first != j->first || !eqValues(i->second, j->second)) return false;
return true; return true;
} }
@ -1046,8 +1047,8 @@ void EvalState::printStats()
% nrEnvs % (nrEnvs * sizeof(Env))); % nrEnvs % (nrEnvs * sizeof(Env)));
printMsg(v, format(" values allocated in environments: %1% (%2% bytes)") printMsg(v, format(" values allocated in environments: %1% (%2% bytes)")
% nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value))); % nrValuesInEnvs % (nrValuesInEnvs * sizeof(Value)));
printMsg(v, format(" values allocated in lists: %1% (%2% bytes)") printMsg(v, format(" list elements: %1% (%2% bytes)")
% nrValuesInLists % (nrValuesInLists * sizeof(Value))); % nrListElems % (nrListElems * sizeof(Value *)));
printMsg(v, format(" misc. values allocated: %1% (%2% bytes) ") printMsg(v, format(" misc. values allocated: %1% (%2% bytes) ")
% nrValues % (nrValues * sizeof(Value))); % nrValues % (nrValues * sizeof(Value)));
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());

View file

@ -76,7 +76,7 @@ struct Value
Bindings * attrs; Bindings * attrs;
struct { struct {
unsigned int length; unsigned int length;
Value * elems; Value * * elems;
} list; } list;
struct { struct {
Env * env; Env * env;
@ -282,8 +282,8 @@ private:
unsigned long nrEnvs; unsigned long nrEnvs;
unsigned long nrValuesInEnvs; unsigned long nrValuesInEnvs;
unsigned long nrValuesInLists;
unsigned long nrValues; unsigned long nrValues;
unsigned long nrListElems;
unsigned long nrEvaluated; unsigned long nrEvaluated;
unsigned int recursionDepth; unsigned int recursionDepth;
unsigned int maxRecursionDepth; unsigned int maxRecursionDepth;

View file

@ -48,7 +48,7 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
} else if (i->second.type == tList) { } else if (i->second.type == tList) {
value.type = MetaValue::tpStrings; value.type = MetaValue::tpStrings;
for (unsigned int j = 0; j < i->second.list.length; ++j) for (unsigned int j = 0; j < i->second.list.length; ++j)
value.stringValues.push_back(state.forceStringNoCtx(i->second.list.elems[j])); value.stringValues.push_back(state.forceStringNoCtx(*i->second.list.elems[j]));
} else continue; } else continue;
meta[i->first] = value; meta[i->first] = value;
} }
@ -206,8 +206,8 @@ static void getDerivations(EvalState & state, Value & vIn,
startNest(nest, lvlDebug, startNest(nest, lvlDebug,
format("evaluating list element")); format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, v.list.elems[n], pathPrefix2, drvs, done)) if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
getDerivations(state, v.list.elems[n], pathPrefix2, autoArgs, drvs, done); getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
} }
} }

View file

@ -311,7 +311,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
if (key == "args") { if (key == "args") {
state.forceList(i->second); state.forceList(i->second);
for (unsigned int n = 0; n < i->second.list.length; ++n) { for (unsigned int n = 0; n < i->second.list.length; ++n) {
string s = state.coerceToString(i->second.list.elems[n], context, true); string s = state.coerceToString(*i->second.list.elems[n], context, true);
drv.args.push_back(s); drv.args.push_back(s);
} }
} }
@ -651,14 +651,17 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
Value * vs = state.allocValues(v.list.length);
StringSet names; StringSet names;
foreach (Bindings::iterator, i, *args[0]->attrs) foreach (Bindings::iterator, i, *args[0]->attrs)
names.insert(i->first); names.insert(i->first);
unsigned int n = 0; unsigned int n = 0;
foreach (StringSet::iterator, i, names) foreach (StringSet::iterator, i, names) {
mkString(v.list.elems[n++], *i); v.list.elems[n] = &vs[n];
mkString(vs[n++], *i);
}
} }
@ -701,8 +704,8 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
state.cloneAttrs(*args[0], v); state.cloneAttrs(*args[0], v);
for (unsigned int i = 0; i < args[1]->list.length; ++i) { for (unsigned int i = 0; i < args[1]->list.length; ++i) {
state.forceStringNoCtx(args[1]->list.elems[i]); state.forceStringNoCtx(*args[1]->list.elems[i]);
v.attrs->erase(state.symbols.create(args[1]->list.elems[i].string.s)); v.attrs->erase(state.symbols.create(args[1]->list.elems[i]->string.s));
} }
} }
@ -718,7 +721,7 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
state.mkAttrs(v); state.mkAttrs(v);
for (unsigned int i = 0; i < args[0]->list.length; ++i) { for (unsigned int i = 0; i < args[0]->list.length; ++i) {
Value & v2(args[0]->list.elems[i]); Value & v2(*args[0]->list.elems[i]);
state.forceAttrs(v2); state.forceAttrs(v2);
Bindings::iterator j = v2.attrs->find(state.sName); Bindings::iterator j = v2.attrs->find(state.sName);
@ -815,8 +818,8 @@ static void prim_head(EvalState & state, Value * * args, Value & v)
state.forceList(*args[0]); state.forceList(*args[0]);
if (args[0]->list.length == 0) if (args[0]->list.length == 0)
throw Error("`head' called on an empty list"); throw Error("`head' called on an empty list");
state.forceValue(args[0]->list.elems[0]); state.forceValue(*args[0]->list.elems[0]);
v = args[0]->list.elems[0]; v = *args[0]->list.elems[0];
} }
@ -840,11 +843,13 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
state.forceList(*args[1]); state.forceList(*args[1]);
state.mkList(v, args[1]->list.length); state.mkList(v, args[1]->list.length);
Value * vs = state.allocValues(v.list.length);
for (unsigned int n = 0; n < v.list.length; ++n) { for (unsigned int n = 0; n < v.list.length; ++n) {
v.list.elems[n].type = tApp; v.list.elems[n] = &vs[n];
v.list.elems[n].app.left = args[0]; vs[n].type = tApp;
v.list.elems[n].app.right = &args[1]->list.elems[n]; vs[n].app.left = args[0];
vs[n].app.right = args[1]->list.elems[n];
} }
} }

View file

@ -97,7 +97,7 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v,
case tList: { case tList: {
XMLOpenElement _(doc, "list"); XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.list.length; ++n)
printValueAsXML(state, strict, v.list.elems[n], doc, context, drvsSeen); printValueAsXML(state, strict, *v.list.elems[n], doc, context, drvsSeen);
break; break;
} }