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:
parent
e392ff53e9
commit
2160258cc4
8 changed files with 78 additions and 37 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ enum Tag {
|
||||||
tEnv,
|
tEnv,
|
||||||
tWithExprEnv,
|
tWithExprEnv,
|
||||||
tWithAttrsEnv,
|
tWithAttrsEnv,
|
||||||
|
tContext,
|
||||||
|
|
||||||
// Value tags
|
// Value tags
|
||||||
tInt,
|
tInt,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue