1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 23:12:44 +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 { namespace nix {
SymbolTable symbols;
// FIXME // FIXME
static char * dupString(const char * s) 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) Value & mkString(Value & v, const string & s, const PathSet & context)
{ {
mkString(v, s.c_str()); mkString(v, s.c_str());
if (!context.empty()) { v.setContext(context);
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;
}
return v; 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 EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
{ {
string s = forceString(v, pos); string s = forceString(v, pos);
copyContext(v, context); v.getContext(context);
return s; return s;
} }
@ -1484,12 +1470,14 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
{ {
string s = forceString(v, pos); string s = forceString(v, pos);
if (v.string.context) { if (v.string.context) {
PathSet context;
v.getContext(context);
if (pos) if (pos)
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%", 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 else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", 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; return s;
} }
@ -1514,7 +1502,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
string s; string s;
if (v.type == tString) { if (v.type == tString) {
copyContext(v, context); v.getContext(context);
return v.string.s; return v.string.s;
} }
@ -1813,9 +1801,6 @@ size_t valueSize(Value & v)
switch (v.type) { switch (v.type) {
case tString: case tString:
sz += doString(v.string.s); sz += doString(v.string.s);
if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
sz += doString(*p);
break; break;
case tPath: case tPath:
sz += doString(v.path); sz += doString(v.path);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ struct Env;
struct Expr; struct Expr;
struct ExprLambda; struct ExprLambda;
struct PrimOp; struct PrimOp;
struct PrimOp; struct Context;
class Symbol; class Symbol;
struct Pos; struct Pos;
class EvalState; class EvalState;
@ -70,6 +70,24 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
#endif #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 struct Value : Object
{ {
union union
@ -94,12 +112,10 @@ struct Value : Object
with context C is used as a derivation attribute, then the with context C is used as a derivation attribute, then the
derivations in C will be added to the inputDrvs of the derivations in C will be added to the inputDrvs of the
derivation, and the other store paths in C will be added to derivation, and the other store paths in C will be added to
the inputSrcs of the derivations. the inputSrcs of the derivations. */
For canonicity, the store paths should be in sorted order. */
struct { struct {
const char * s; const char * s;
const char * * context; // must be in sorted order Context * context;
} string; } string;
const char * path; const char * path;
@ -152,6 +168,35 @@ public:
} }
constexpr static Size words() { return 3; } // FIXME 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]);
}
}
}
}; };