optimize record construction by deferring initializations of aggregates

This commit is contained in:
Vern Paxson 2023-04-10 11:44:11 -07:00
parent 2e2afa5e11
commit 0787c130d0
5 changed files with 190 additions and 158 deletions

View file

@ -992,39 +992,102 @@ void TypeDecl::DescribeReST(ODesc* d, bool roles_only) const
}
}
// The following tracks how to initialize a given field, for fast execution
// of Create().
class FieldInit
class DirectFieldInit final : public FieldInit
{
public:
// The type of initialization for the field.
enum
DirectFieldInit(ZVal _init_val) : init_val(_init_val) { }
ZVal Generate() const override { return init_val; }
private:
ZVal init_val;
};
class DirectManagedFieldInit final : public FieldInit
{
public:
DirectManagedFieldInit(ZVal _init_val) : init_val(_init_val) { }
~DirectManagedFieldInit() { ZVal::DeleteManagedType(init_val); }
ZVal Generate() const override
{
R_INIT_NONE, // skip this entry
zeek::Ref(init_val.ManagedVal());
return init_val;
}
R_INIT_DIRECT, // look in direct_init for raw value
R_INIT_DIRECT_MANAGED, // same, but managed type
private:
ZVal init_val;
};
R_INIT_DEF, // look in def_expr for expression
class ExprFieldInit final : public FieldInit
{
public:
// Initialization requires evaluating the given expression,
// yielding the a value of the given type (which might require
// coercion for some records).
ExprFieldInit(detail::ExprPtr _init_expr, TypePtr _init_type)
: init_expr(std::move(_init_expr)), init_type(std::move(_init_type))
{
if ( init_type->Tag() == TYPE_RECORD && ! same_type(init_expr->GetType(), init_type) )
coerce_type = cast_intrusive<RecordType>(init_type);
}
R_INIT_RECORD, // field requires a new record
R_INIT_TABLE, // field requires a new table/set
R_INIT_VECTOR, // field requires a new vector
} init_type = R_INIT_NONE;
ZVal Generate() const override
{
auto v = init_expr->Eval(nullptr);
if ( ! v )
{
reporter->Error("failed &default in record creation");
return ZVal();
}
bool def_coerce = false; // whether coercion's required
if ( coerce_type )
v = v->AsRecordVal()->CoerceTo(coerce_type);
// For R_INIT_DIRECT/R_INIT_DIRECT_MANAGED:
ZVal direct_init;
return ZVal(v, init_type);
}
detail::ExprPtr def_expr;
TypePtr def_type;
private:
detail::ExprPtr init_expr;
TypePtr init_type;
RecordTypePtr coerce_type; // non-nil iff coercion is required
};
RecordTypePtr r_type; // for R_INIT_RECORD
TableTypePtr t_type; // for R_INIT_TABLE
detail::AttributesPtr attrs; // attributes for R_INIT_TABLE
VectorTypePtr v_type; // for R_INIT_VECTOR
class RecordFieldInit final : public FieldInit
{
public:
RecordFieldInit(RecordTypePtr _init_type) : init_type(std::move(_init_type)) { }
ZVal Generate() const override { return ZVal(new RecordVal(init_type)); }
private:
RecordTypePtr init_type;
};
class TableFieldInit final : public FieldInit
{
public:
TableFieldInit(TableTypePtr _init_type, detail::AttributesPtr _attrs)
: init_type(std::move(_init_type)), attrs(std::move(_attrs))
{
}
ZVal Generate() const override { return ZVal(new TableVal(init_type, attrs)); }
private:
TableTypePtr init_type;
detail::AttributesPtr attrs;
};
class VectorFieldInit final : public FieldInit
{
public:
VectorFieldInit(VectorTypePtr _init_type) : init_type(std::move(_init_type)) { }
ZVal Generate() const override { return ZVal(new VectorVal(init_type)); }
private:
VectorTypePtr init_type;
};
RecordType::RecordType(type_decl_list* arg_types) : Type(TYPE_RECORD)
@ -1064,7 +1127,8 @@ RecordType::~RecordType()
}
for ( auto fi : field_inits )
delete fi;
if ( fi )
delete *fi;
}
void RecordType::AddField(unsigned int field, const TypeDecl* td)
@ -1074,53 +1138,43 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td)
managed_fields.push_back(ZVal::IsManagedType(td->type));
auto init = new FieldInit();
init->init_type = FieldInit::R_INIT_NONE;
init->attrs = td->attrs;
// We defer error-checking until here so that we can keep field_inits
// and managed_fields correctly tracking the associated fields.
if ( field_ids.count(td->id) != 0 )
{
reporter->Error("duplicate field '%s' found in record definition", td->id);
field_inits.push_back(init);
field_inits.push_back(std::nullopt);
return;
}
field_ids.insert(std::string(td->id));
auto a = init->attrs;
auto a = td->attrs;
auto type = td->type;
auto def_attr = a ? a->Find(detail::ATTR_DEFAULT) : nullptr;
auto def_expr = def_attr ? def_attr->GetExpr() : nullptr;
std::optional<FieldInit*> init;
if ( def_expr && ! IsErrorType(type->Tag()) )
{
if ( type->Tag() == TYPE_RECORD && def_expr->GetType()->Tag() == TYPE_RECORD &&
! same_type(def_expr->GetType(), type) )
init->def_coerce = true;
if ( def_expr->Tag() == detail::EXPR_CONST )
{
auto v = def_expr->Eval(nullptr);
auto zv = ZVal(v, type);
if ( ZVal::IsManagedType(type) )
init->init_type = FieldInit::R_INIT_DIRECT_MANAGED;
init = new DirectManagedFieldInit(zv);
else
init->init_type = FieldInit::R_INIT_DIRECT;
init->direct_init = ZVal(v, type);
init = new DirectFieldInit(zv);
}
else
{
init->init_type = FieldInit::R_INIT_DEF;
init->def_expr = def_expr;
init->def_type = def_expr->GetType();
auto efi = new ExprFieldInit(def_expr, type);
field_expr_inits.emplace_back(std::make_pair(field, efi));
}
}
@ -1129,22 +1183,13 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td)
TypeTag tag = type->Tag();
if ( tag == TYPE_RECORD )
{
init->init_type = FieldInit::R_INIT_RECORD;
init->r_type = cast_intrusive<RecordType>(type);
}
init = new RecordFieldInit(cast_intrusive<RecordType>(type));
else if ( tag == TYPE_TABLE )
{
init->init_type = FieldInit::R_INIT_TABLE;
init->t_type = cast_intrusive<TableType>(type);
}
init = new TableFieldInit(cast_intrusive<TableType>(type), a);
else if ( tag == TYPE_VECTOR )
{
init->init_type = FieldInit::R_INIT_VECTOR;
init->v_type = cast_intrusive<VectorType>(type);
}
init = new VectorFieldInit(cast_intrusive<VectorType>(type));
}
field_inits.push_back(init);
@ -1342,68 +1387,6 @@ void RecordType::AddFieldsDirectly(const type_decl_list& others, bool add_log_at
num_fields = types->length();
}
void RecordType::Create(std::vector<std::optional<ZVal>>& r) const
{
int n = NumFields();
for ( int i = 0; i < n; ++i )
{
auto* init = field_inits[i];
ZVal r_i;
switch ( init->init_type )
{
case FieldInit::R_INIT_NONE:
r.push_back(std::nullopt);
continue;
case FieldInit::R_INIT_DIRECT:
r_i = init->direct_init;
break;
case FieldInit::R_INIT_DIRECT_MANAGED:
r_i = init->direct_init;
zeek::Ref(r_i.ManagedVal());
break;
case FieldInit::R_INIT_DEF:
{
auto v = init->def_expr->Eval(nullptr);
if ( v )
{
const auto& t = init->def_type;
if ( init->def_coerce )
{
auto rt = cast_intrusive<RecordType>(t);
v = v->AsRecordVal()->CoerceTo(rt);
}
r_i = ZVal(v, t);
}
else
reporter->Error("failed &default in record creation");
}
break;
case FieldInit::R_INIT_RECORD:
r_i = ZVal(new RecordVal(init->r_type));
break;
case FieldInit::R_INIT_TABLE:
r_i = ZVal(new TableVal(init->t_type, init->attrs));
break;
case FieldInit::R_INIT_VECTOR:
r_i = ZVal(new VectorVal(init->v_type));
break;
}
r.push_back(r_i);
}
}
void RecordType::DescribeFields(ODesc* d) const
{
if ( d->IsReadable() )