1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 15:02:42 +01:00

Store contexts as symbols

This provides some deduplication since most contexts are used multiple
times.

Also, store singleton contexts directly in the Value object. This
saves a 16-byte Context object. This is useful because the vast
majority of contexts are singletons, e.g. 23723 out of 26138 in a
NixOS 19.03 system configuration.
This commit is contained in:
Eelco Dolstra 2019-04-23 11:07:47 +02:00
parent e392ff53e9
commit 2160258cc4
8 changed files with 78 additions and 37 deletions

View file

@ -21,6 +21,8 @@
namespace nix {
SymbolTable symbols;
// FIXME
static char * dupString(const char * s)
@ -500,15 +502,7 @@ void mkString(Value & v, const char * s)
Value & mkString(Value & v, const string & s, const PathSet & context)
{
mkString(v, s.c_str());
if (!context.empty()) {
size_t n = 0;
// FIXME: store as Symbols?
v.string.context = (const char * *)
malloc((context.size() + 1) * sizeof(char *));
for (auto & i : context)
v.string.context[n++] = dupString(i.c_str());
v.string.context[n] = 0;
}
v.setContext(context);
return v;
}
@ -1464,18 +1458,10 @@ string EvalState::forceString(Value & v, const Pos & pos)
}
void copyContext(const Value & v, PathSet & context)
{
if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
context.insert(*p);
}
string EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{
string s = forceString(v, pos);
copyContext(v, context);
v.getContext(context);
return s;
}
@ -1484,12 +1470,14 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{
string s = forceString(v, pos);
if (v.string.context) {
PathSet context;
v.getContext(context);
if (pos)
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
v.string.s, v.string.context[0], pos);
v.string.s, *context.begin(), pos);
else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0]);
v.string.s, *context.begin());
}
return s;
}
@ -1514,7 +1502,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
string s;
if (v.type == tString) {
copyContext(v, context);
v.getContext(context);
return v.string.s;
}
@ -1813,9 +1801,6 @@ size_t valueSize(Value & v)
switch (v.type) {
case tString:
sz += doString(v.string.s);
if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
sz += doString(*p);
break;
case tPath:
sz += doString(v.path);

View file

@ -84,8 +84,6 @@ public:
Value & mkString(Value & v, const string & s, const PathSet & context = PathSet());
void copyContext(const Value & v, PathSet & context);
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
@ -106,7 +104,7 @@ void initGC();
class EvalState
{
public:
SymbolTable symbols;
SymbolTable & symbols{nix::symbols}; // FIXME: remove
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,

View file

@ -100,6 +100,7 @@ void GC::gc()
break;
}
case tContext:
case tInt:
case tBool:
case tNull:
@ -107,9 +108,14 @@ void GC::gc()
case tFloat:
break;
case tString:
// FIXME
case tString: {
auto obj2 = (Value *) obj;
// FIXME: GC string
// See setContext().
if (!(((ptrdiff_t) obj2->string.context) & 1))
push(obj2->string.context);
break;
}
case tPath:
// FIXME
@ -245,9 +251,12 @@ std::pair<Size, Size> GC::Arena::freeUnmarked()
case tWithAttrsEnv:
objSize = ((Env *) obj)->words();
break;
case tContext:
objSize = ((Context *) obj)->getSize() + 1;
break;
default:
printError("GC encountered invalid object with tag %d", tag);
abort();
//throw Error("GC encountered invalid object with tag %d", tag);
}
}

View file

@ -20,6 +20,7 @@ enum Tag {
tEnv,
tWithExprEnv,
tWithAttrsEnv,
tContext,
// Value tags
tInt,

View file

@ -19,6 +19,7 @@ private:
const string * s; // pointer into SymbolTable
Symbol(const string * s) : s(s) { };
friend class SymbolTable;
friend class Value;
public:
Symbol() : s(0) { };
@ -84,4 +85,6 @@ public:
}
};
extern SymbolTable symbols;
}

View file

@ -27,7 +27,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
case tString:
copyContext(v, context);
v.getContext(context);
out.write(v.string.s);
break;

View file

@ -70,7 +70,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
case tString:
/* !!! show the context? */
copyContext(v, context);
v.getContext(context);
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
break;

View file

@ -10,7 +10,7 @@ struct Env;
struct Expr;
struct ExprLambda;
struct PrimOp;
struct PrimOp;
struct Context;
class Symbol;
struct Pos;
class EvalState;
@ -70,6 +70,24 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
#endif
class Context : Object
{
friend class Value;
friend class GC;
Symbol members[0];
Context(const PathSet & context) : Object(tContext, context.size())
{
size_t n = 0;
for (auto & i : context)
members[n++] = symbols.create(i);
}
size_t getSize() { return getMisc(); }
};
struct Value : Object
{
union
@ -94,12 +112,10 @@ struct Value : Object
with context C is used as a derivation attribute, then the
derivations in C will be added to the inputDrvs of the
derivation, and the other store paths in C will be added to
the inputSrcs of the derivations.
For canonicity, the store paths should be in sorted order. */
the inputSrcs of the derivations. */
struct {
const char * s;
const char * * context; // must be in sorted order
Context * context;
} string;
const char * path;
@ -152,6 +168,35 @@ public:
}
constexpr static Size words() { return 3; } // FIXME
void setContext(const PathSet & context)
{
if (context.size() == 0)
string.context = nullptr;
else if (context.size() == 1) {
// If we have a single context, then store it
// directly. This saves allocating a Context object (16
// bytes).
auto symbol = symbols.create(*context.begin());
string.context = (Context *) (((ptrdiff_t) symbol.s) | 1);
} else {
string.context = gc.alloc<Context>(1 + context.size(), context);
}
}
void getContext(PathSet & context)
{
if (string.context) {
if (((ptrdiff_t) string.context) & 1) {
auto s = (const std::string *) (((ptrdiff_t) string.context) & ~1UL);
context.insert(*s);
} else {
auto size = string.context->getSize();
for (size_t i = 0; i < size; ++i)
context.insert(string.context->members[i]);
}
}
}
};