Compare commits

...

4 commits

Author SHA1 Message Date
Nicolas Pierron 6468f63994 Fix typo. 2009-07-08 09:44:22 +00:00
Nicolas Pierron 9416202465 Add a way to cache result for future evaluations. 2009-07-08 09:27:35 +00:00
Nicolas Pierron 4941ba3413 Add a session independent cache. 2009-07-08 09:26:15 +00:00
Nicolas Pierron 7244b9b861 Create a cache of reduce operations which are session independent. 2009-07-08 09:18:26 +00:00
11 changed files with 239 additions and 14 deletions

View file

@ -3,7 +3,7 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \ libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \ nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \ get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \
names.cc names.cc normal-forms.cc
pkginclude_HEADERS = \ pkginclude_HEADERS = \
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \ nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \

View file

@ -16,15 +16,23 @@ namespace nix {
EvalState::EvalState() EvalState::EvalState()
: normalForms(32768), primOps(128) : sessionNormalForms(32768), normalForms(32768), primOps(128)
{ {
nrEvaluated = nrCached = 0; nrEvaluated = nrCached = nrDepthAfterReset = 0;
initNixExprHelpers(); initNixExprHelpers();
addPrimOps(); addPrimOps();
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == ""; allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
safeCache = true;
loadNormalForms();
}
EvalState::~EvalState()
{
saveNormalForms();
} }
@ -468,8 +476,10 @@ LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
if (arity == 0) if (arity == 0)
/* !!! backtrace for primop call */ /* !!! backtrace for primop call */
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector()); return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
else else {
state.safeCache = false;
return makePrimOp(arity, fun, ATempty); return makePrimOp(arity, fun, ATempty);
}
} }
@ -495,6 +505,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
for (ATermIterator i(args); i; ++i) for (ATermIterator i(args); i; ++i)
args2[--arity] = *i; args2[--arity] = *i;
/* !!! backtrace for primop call */ /* !!! backtrace for primop call */
state.safeCache = true;
return ((PrimOp) ATgetBlobData(funBlob)) return ((PrimOp) ATgetBlobData(funBlob))
(state, args2); (state, args2);
} else } else
@ -802,11 +813,19 @@ Expr evalExpr(EvalState & state, Expr e)
format("evaluating expression: %1%") % e); format("evaluating expression: %1%") % e);
#endif #endif
state.nrEvaluated++; const unsigned int hugeEvalExpr = 100;
unsigned int nrEvaluated = state.nrEvaluated++;
state.nrDepthAfterReset++;
/* Consult the memo table to quickly get the normal form of /* Consult the memo table to quickly get the normal form of
previously evaluated expressions. */ previously evaluated expressions. */
Expr nf = state.normalForms.get(e); Expr nf = state.normalForms.get(e);
if (nf) {
state.nrCached++;
return nf;
}
nf = state.sessionNormalForms.get(e);
if (nf) { if (nf) {
if (nf == makeBlackHole()) if (nf == makeBlackHole())
throwEvalError("infinite recursion encountered"); throwEvalError("infinite recursion encountered");
@ -815,14 +834,25 @@ Expr evalExpr(EvalState & state, Expr e)
} }
/* Otherwise, evaluate and memoize. */ /* Otherwise, evaluate and memoize. */
state.normalForms.set(e, makeBlackHole()); state.sessionNormalForms.set(e, makeBlackHole());
try { try {
nf = evalExpr2(state, e); nf = evalExpr2(state, e);
} catch (Error & err) { } catch (Error & err) {
state.normalForms.remove(e); state.sessionNormalForms.remove(e);
throw; throw;
} }
state.normalForms.set(e, nf);
// session independent condition
if (state.nrDepthAfterReset && state.safeCache &&
// heuristic condition
state.nrEvaluated - nrEvaluated > hugeEvalExpr) {
state.sessionNormalForms.remove(e);
state.normalForms.set(e, nf);
state.nrDepthAfterReset--;
} else {
state.sessionNormalForms.set(e, nf);
}
return nf; return nf;
} }
@ -830,6 +860,7 @@ Expr evalExpr(EvalState & state, Expr e)
Expr evalFile(EvalState & state, const Path & path) Expr evalFile(EvalState & state, const Path & path)
{ {
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path); startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
state.nrDepthAfterReset = 0;
Expr e = parseExprFromFile(state, path); Expr e = parseExprFromFile(state, path);
try { try {
return evalExpr(state, e); return evalExpr(state, e);
@ -901,11 +932,13 @@ void printEvalStats(EvalState & state)
char x; char x;
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0"; bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
printMsg(showStats ? lvlInfo : lvlDebug, printMsg(showStats ? lvlInfo : lvlDebug,
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space") format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% cached normal reduction, %7% session dependent reduction.")
% state.nrEvaluated % state.nrCached % state.nrEvaluated % state.nrCached
% ((float) state.nrCached / (float) state.nrEvaluated * 100) % ((float) state.nrCached / (float) state.nrEvaluated * 100)
% AT_calcAllocatedSize() % AT_calcAllocatedSize()
% (&x - deepestStack)); % (&x - deepestStack)
% state.normalForms.size()
% state.sessionNormalForms.size());
if (showStats) if (showStats)
printATermMapStats(); printATermMapStats();
} }

View file

@ -30,6 +30,7 @@ typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
struct EvalState struct EvalState
{ {
ATermMap normalForms; ATermMap normalForms;
ATermMap sessionNormalForms;
ATermMap primOps; ATermMap primOps;
DrvRoots drvRoots; DrvRoots drvRoots;
DrvHashes drvHashes; /* normalised derivation hashes */ DrvHashes drvHashes; /* normalised derivation hashes */
@ -37,14 +38,19 @@ struct EvalState
unsigned int nrEvaluated; unsigned int nrEvaluated;
unsigned int nrCached; unsigned int nrCached;
unsigned int nrDepthAfterReset;
bool allowUnsafeEquality; bool allowUnsafeEquality;
bool safeCache;
EvalState(); EvalState();
~EvalState();
void addPrimOps(); void addPrimOps();
void addPrimOp(const string & name, void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp); unsigned int arity, PrimOp primOp);
void loadNormalForms();
void saveNormalForms();
}; };

View file

@ -0,0 +1,64 @@
#include "eval.hh"
#include "util.hh"
#include "globals.hh"
#include "serialise.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix {
void EvalState::loadNormalForms()
{
// nixCacheFile = getEnv("NIX_CACHE_FILE", nixStateDir + "/reduce-cache");
nixCacheFile = getEnv("NIX_CACHE_FILE");
string loadFlag = getEnv("NIX_CACHE_FILE_LOAD", "1");
if(nixCacheFile == "" || loadFlag == "")
return;
if(!pathExists(nixCacheFile))
return;
printMsg(lvlInfo, format("Load cache: ..."));
try {
int fd = open(nixCacheFile.c_str(), O_RDONLY);
if (fd == -1)
throw SysError(format("opening file `%1%'") % nixCacheFile);
AutoCloseFD auto_fd = fd;
FdSource source = fd;
normalForms = readATermMap(source);
} catch (Error & e) {
e.addPrefix(format("Cannot load cached reduce operations from %1%:\n") % nixCacheFile);
throw;
}
printMsg(lvlInfo, format("Load cache: end"));
}
void EvalState::saveNormalForms()
{
string saveFlag = getEnv("NIX_CACHE_FILE_SAVE", "");
if(nixCacheFile == "" || saveFlag == "")
return;
printMsg(lvlInfo, format("Save cache: ..."));
try {
int fd = open(nixCacheFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (fd == -1)
throw SysError(format("opening file `%1%'") % nixCacheFile);
AutoCloseFD auto_fd = fd;
FdSink sink = fd;
writeATermMap(normalForms, sink);
} catch (Error & e) {
e.addPrefix(format("Cannot save cached reduce operations to %1%:\n") % nixCacheFile);
throw;
}
printMsg(lvlInfo, format("Save cache: end"));
}
}

View file

@ -74,12 +74,14 @@ static Expr prim_null(EvalState & state, const ATermVector & args)
platforms. */ platforms. */
static Expr prim_currentSystem(EvalState & state, const ATermVector & args) static Expr prim_currentSystem(EvalState & state, const ATermVector & args)
{ {
state.nrDepthAfterReset = 0;
return makeStr(thisSystem); return makeStr(thisSystem);
} }
static Expr prim_currentTime(EvalState & state, const ATermVector & args) static Expr prim_currentTime(EvalState & state, const ATermVector & args)
{ {
state.nrDepthAfterReset = 0;
return ATmake("Int(<int>)", time(0)); return ATmake("Int(<int>)", time(0));
} }
@ -228,6 +230,7 @@ static Expr prim_addErrorContext(EvalState & state, const ATermVector & args)
static Expr prim_getEnv(EvalState & state, const ATermVector & args) static Expr prim_getEnv(EvalState & state, const ATermVector & args)
{ {
string name = evalStringNoCtx(state, args[0]); string name = evalStringNoCtx(state, args[0]);
state.nrDepthAfterReset = 0;
return makeStr(getEnv(name)); return makeStr(getEnv(name));
} }
@ -316,6 +319,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv)
static Expr prim_derivationStrict(EvalState & state, const ATermVector & args) static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
{ {
startNest(nest, lvlVomit, "evaluating derivation"); startNest(nest, lvlVomit, "evaluating derivation");
state.nrDepthAfterReset = 0;
ATermMap attrs; ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs, true); queryAllAttrs(evalExpr(state, args[0]), attrs, true);
@ -549,6 +553,7 @@ static Expr prim_storePath(EvalState & state, const ATermVector & args)
{ {
PathSet context; PathSet context;
Path path = canonPath(coerceToPath(state, args[0], context)); Path path = canonPath(coerceToPath(state, args[0], context));
state.nrDepthAfterReset = 0;
if (!isInStore(path)) if (!isInStore(path))
throw EvalError(format("path `%1%' is not in the Nix store") % path); throw EvalError(format("path `%1%' is not in the Nix store") % path);
Path path2 = toStorePath(path); Path path2 = toStorePath(path);
@ -565,6 +570,7 @@ static Expr prim_pathExists(EvalState & state, const ATermVector & args)
Path path = coerceToPath(state, args[0], context); Path path = coerceToPath(state, args[0], context);
if (!context.empty()) if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path); throw EvalError(format("string `%1%' cannot refer to other paths") % path);
state.nrDepthAfterReset = 0;
return makeBool(pathExists(path)); return makeBool(pathExists(path));
} }
@ -598,6 +604,7 @@ static Expr prim_readFile(EvalState & state, const ATermVector & args)
Path path = coerceToPath(state, args[0], context); Path path = coerceToPath(state, args[0], context);
if (!context.empty()) if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path); throw EvalError(format("string `%1%' cannot refer to other paths") % path);
state.nrDepthAfterReset = 0;
return makeStr(readFile(path)); return makeStr(readFile(path));
} }
@ -637,6 +644,7 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
refs.insert(path); refs.insert(path);
} }
state.nrDepthAfterReset = 0;
Path storePath = readOnlyMode Path storePath = readOnlyMode
? computeStorePathForText(name, contents, refs) ? computeStorePathForText(name, contents, refs)
: store->addTextToStore(name, contents, refs); : store->addTextToStore(name, contents, refs);
@ -689,6 +697,7 @@ static Expr prim_filterSource(EvalState & state, const ATermVector & args)
FilterFromExpr filter(state, args[0]); FilterFromExpr filter(state, args[0]);
state.nrDepthAfterReset = 0;
Path dstPath = readOnlyMode Path dstPath = readOnlyMode
? computeStorePathForPath(path, true, htSHA256, filter).first ? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter); : store->addToStore(path, true, htSHA256, filter);

View file

@ -13,6 +13,7 @@ string nixDataDir = "/UNINIT";
string nixLogDir = "/UNINIT"; string nixLogDir = "/UNINIT";
string nixStateDir = "/UNINIT"; string nixStateDir = "/UNINIT";
string nixDBPath = "/UNINIT"; string nixDBPath = "/UNINIT";
string nixCacheFile = "/UNINIT";
string nixConfDir = "/UNINIT"; string nixConfDir = "/UNINIT";
string nixLibexecDir = "/UNINIT"; string nixLibexecDir = "/UNINIT";
string nixBinDir = "/UNINIT"; string nixBinDir = "/UNINIT";

View file

@ -24,6 +24,9 @@ extern string nixStateDir;
/* nixDBPath is the path name of our Berkeley DB environment. */ /* nixDBPath is the path name of our Berkeley DB environment. */
extern string nixDBPath; extern string nixDBPath;
/* nixCacheFile is the path name of the normal form cache. */
extern string nixCacheFile;
/* nixConfDir is the directory where configuration files are /* nixConfDir is the directory where configuration files are
stored. */ stored. */
extern string nixConfDir; extern string nixConfDir;

View file

@ -215,7 +215,7 @@ void ATermMap::remove(ATerm key)
} }
unsigned int ATermMap::size() unsigned int ATermMap::size() const
{ {
return count; /* STL nomenclature */ return count; /* STL nomenclature */
} }

View file

@ -59,7 +59,7 @@ public:
void remove(ATerm key); void remove(ATerm key);
unsigned int size(); unsigned int size() const;
struct const_iterator struct const_iterator
{ {

View file

@ -1,9 +1,9 @@
#include "serialise.hh" #include "serialise.hh"
#include "util.hh" #include "util.hh"
#include "aterm.hh"
#include <cstring> #include <cstring>
namespace nix { namespace nix {
@ -73,6 +73,67 @@ void writeStringSet(const StringSet & ss, Sink & sink)
} }
void writeATerm(ATerm t, Sink & sink)
{
int len;
unsigned char *buf = (unsigned char *) ATwriteToBinaryString(t, &len);
AutoDeleteArray<unsigned char> d(buf);
writeInt(len, sink);
sink(buf, len);
}
/* convert the ATermMap to a list of couple because many terms are shared
between the keys and between the values. Thus the BAF stored by
writeATerm consume less memory space. The list of couples is saved
inside a tree structure of /treedepth/ height because the serialiasation
of ATerm cause a tail recurssion on list tails. */
void writeATermMap(const ATermMap & tm, Sink & sink)
{
const unsigned int treedepth = 5;
const unsigned int maxarity = 128; // 2 < maxarity < MAX_ARITY (= 255)
const unsigned int bufsize = treedepth * maxarity;
AFun map = ATmakeAFun("map", 2, ATfalse);
AFun node = ATmakeAFun("node", maxarity, ATfalse);
ATerm empty = (ATerm) ATmakeAppl0(ATmakeAFun("empty", 0, ATfalse));
unsigned int c[treedepth];
ATerm *buf = new ATerm[bufsize];
AutoDeleteArray<ATerm> d(buf);
memset(buf, 0, bufsize * sizeof(ATerm));
ATprotectArray(buf, bufsize);
for (unsigned int j = 0; j < treedepth; j++)
c[j] = 0;
for (ATermMap::const_iterator i = tm.begin(); i != tm.end(); ++i) {
unsigned int depth = treedepth - 1;
ATerm term = (ATerm) ATmakeAppl2(map, i->key, i->value);
buf[depth * maxarity + c[depth]++] = term;
while (c[depth] % maxarity == 0) {
c[depth] = 0;
term = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
depth--;
buf[depth * maxarity + c[depth]++] = term;
}
}
unsigned int depth = treedepth;
ATerm last_node = empty;
while (depth--) {
buf[depth * maxarity + c[depth]++] = last_node;
while (c[depth] % maxarity)
buf[depth * maxarity + c[depth]++] = empty;
last_node = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
}
writeATerm(last_node, sink);
ATunprotectArray(buf);
}
void readPadding(unsigned int len, Source & source) void readPadding(unsigned int len, Source & source)
{ {
if (len % 8) { if (len % 8) {
@ -136,4 +197,48 @@ StringSet readStringSet(Source & source)
} }
ATerm readATerm(Source & source)
{
unsigned int len = readInt(source);
unsigned char * buf = new unsigned char[len];
AutoDeleteArray<unsigned char> d(buf);
source(buf, len);
ATerm t = ATreadFromBinaryString((char *) buf, len);
if (t == 0)
throw SerialisationError("cannot read a valid ATerm.");
return t;
}
static void recursiveInitATermMap(ATermMap &tm, bool &stop, ATermAppl node)
{
const unsigned int arity = ATgetArity(ATgetAFun(node));
ATerm key, value;
switch (arity) {
case 0:
stop = true;
return;
case 2:
key = ATgetArgument(node, 0);
value = ATgetArgument(node, 1);
tm.set(key, value);
return;
default:
for (unsigned int i = 0; i < arity && !stop; i++)
recursiveInitATermMap(tm, stop, (ATermAppl) ATgetArgument(node, i));
return;
}
}
ATermMap readATermMap(Source & source)
{
ATermMap tm;
bool stop = false;
recursiveInitATermMap(tm, stop, (ATermAppl) readATerm(source));
return tm;
}
} }

View file

@ -2,7 +2,7 @@
#define __SERIALISE_H #define __SERIALISE_H
#include "types.hh" #include "types.hh"
#include "aterm-map.hh"
namespace nix { namespace nix {
@ -98,12 +98,16 @@ void writeInt(unsigned int n, Sink & sink);
void writeLongLong(unsigned long long n, Sink & sink); void writeLongLong(unsigned long long n, Sink & sink);
void writeString(const string & s, Sink & sink); void writeString(const string & s, Sink & sink);
void writeStringSet(const StringSet & ss, Sink & sink); void writeStringSet(const StringSet & ss, Sink & sink);
void writeATerm(ATerm t, Sink & sink);
void writeATermMap(const ATermMap & tm, Sink & sink);
void readPadding(unsigned int len, Source & source); void readPadding(unsigned int len, Source & source);
unsigned int readInt(Source & source); unsigned int readInt(Source & source);
unsigned long long readLongLong(Source & source); unsigned long long readLongLong(Source & source);
string readString(Source & source); string readString(Source & source);
StringSet readStringSet(Source & source); StringSet readStringSet(Source & source);
ATerm readATerm(Source & source);
ATermMap readATermMap(Source & source);
MakeError(SerialisationError, Error) MakeError(SerialisationError, Error)