Skip to content

Commit

Permalink
Add arena enabled copy construction.
Browse files Browse the repository at this point in the history
Arena enabled copy construction provides efficiency gains for the rather common construct where a message is currently copied by first creating a default initialized instance through the regular (arena) constructor, and then recursively filled from the source message using 'MergeFrom' or 'CopyFrom'.

Arena enabled copy construction is feature gated in port_def.inc and currently not enabled by default.

PiperOrigin-RevId: 558854125
  • Loading branch information
martijnvels authored and copybara-github committed Aug 21, 2023
1 parent ef58724 commit a2b3470
Show file tree
Hide file tree
Showing 13 changed files with 930 additions and 17 deletions.
60 changes: 54 additions & 6 deletions src/google/protobuf/compiler/cpp/field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc)
: descriptor_(descriptor), options_(options) {
bool is_repeated_or_map = descriptor->is_repeated();
should_split_ = ShouldSplit(descriptor, options);
is_oneof_ = descriptor->real_containing_oneof() != nullptr;
switch (descriptor->cpp_type()) {
Expand All @@ -132,26 +133,71 @@ FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
case FieldDescriptor::CPPTYPE_FLOAT:
case FieldDescriptor::CPPTYPE_DOUBLE:
case FieldDescriptor::CPPTYPE_BOOL:
is_trivial_ = !(descriptor->is_repeated() || descriptor->is_map());
has_trivial_value_ = is_trivial_;
is_trivial_ = has_trivial_value_ = !is_repeated_or_map;
has_default_constexpr_constructor_ = is_repeated_or_map;
break;
case FieldDescriptor::CPPTYPE_STRING:
is_string_ = true;
string_type_ = descriptor->options().ctype();
is_inlined_ = IsStringInlined(descriptor, options);
is_bytes_ = descriptor->type() == FieldDescriptor::TYPE_BYTES;
has_default_constexpr_constructor_ = is_repeated_or_map;
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
is_message_ = true;
is_group_ = descriptor->type() == FieldDescriptor::TYPE_GROUP;
is_foreign_ = IsCrossFileMessage(descriptor);
is_lazy_ = IsLazy(descriptor, options, scc);
is_weak_ = IsImplicitWeakField(descriptor, options, scc);
if (!(descriptor->is_repeated() || descriptor->is_map())) {
has_trivial_value_ = !is_lazy_;
}
is_lazy_ = IsLazy(descriptor, options, scc);
has_trivial_value_ = !(is_repeated_or_map || is_lazy_);
has_default_constexpr_constructor_ = is_repeated_or_map || is_lazy_;
break;
}

has_trivial_zero_default_ = CanInitializeByZeroing(descriptor, options, scc);
}

void FieldGeneratorBase::GenerateMemberConstexprConstructor(
io::Printer* p) const {
ABSL_CHECK(!descriptor_->is_extension());
if (descriptor_->is_repeated()) {
p->Emit("$name$_{}");
} else {
p->Emit({{"default", DefaultValue(options_, descriptor_)}},
"$name$_{$default$}");
}
}

void FieldGeneratorBase::GenerateMemberConstructor(io::Printer* p) const {
ABSL_CHECK(!descriptor_->is_extension());
if (descriptor_->is_map()) {
p->Emit("$name$_{visibility, arena}");
} else if (descriptor_->is_repeated()) {
if (ShouldSplit(descriptor_, options_)) {
p->Emit("$name$_{}"); // RawPtr<Repeated>
} else {
p->Emit("$name$_{visibility, arena}");
}
} else {
p->Emit({{"default", DefaultValue(options_, descriptor_)}},
"$name$_{$default$}");
}
}

void FieldGeneratorBase::GenerateMemberCopyConstructor(io::Printer* p) const {
ABSL_CHECK(!descriptor_->is_extension());
if (descriptor_->is_repeated()) {
p->Emit("$name$_{visibility, arena, from.$name$_}");
} else {
p->Emit("$name$_{from.$name$_}");
}
}

void FieldGeneratorBase::GenerateOneofCopyConstruct(io::Printer* p) const {
ABSL_CHECK(!descriptor_->is_extension()) << "Not supported";
ABSL_CHECK(!descriptor_->is_repeated()) << "Not supported";
ABSL_CHECK(!descriptor_->is_map()) << "Not supported";
p->Emit("$field$ = from.$field$;\n");
}

void FieldGeneratorBase::GenerateAggregateInitializer(io::Printer* p) const {
Expand Down Expand Up @@ -275,6 +321,8 @@ void InlinedStringVars(const FieldDescriptor* field, const Options& opts,

int32_t index = *idx / 32;
std::string mask = absl::StrFormat("0x%08xu", 1u << (*idx % 32));
vars.emplace_back("inlined_string_index", index);
vars.emplace_back("inlined_string_mask", mask);

absl::string_view array = IsMapEntryMessage(field->containing_type())
? "_inlined_string_donated_"
Expand Down
81 changes: 80 additions & 1 deletion src/google/protobuf/compiler/cpp/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ class FieldGeneratorBase {
// trivial, or a (raw) pointer value to a singular, non lazy message.
bool has_trivial_value() const { return has_trivial_value_; }

// Returns true if the provided field has a trivial zero default.
// I.e., the field can be initialized with `memset(&field, 0, sizeof(field))`
bool has_trivial_zero_default() const { return has_trivial_zero_default_; }

// Returns true if the field is a singular or repeated message.
// This includes group message types. To explicitly check if a message
// type is a group type, use the `is_group()` function,
Expand Down Expand Up @@ -113,6 +117,12 @@ class FieldGeneratorBase {
// allocated. Applies to string and message value.
bool is_inlined() const { return is_inlined_; }

// Returns true if this field has an appropriate default constexpr
// constructor. i.e., there is no need for an explicit initializer.
bool has_default_constexpr_constructor() const {
return has_default_constexpr_constructor_;
}

virtual std::vector<io::Printer::Sub> MakeVars() const { return {}; }

virtual void GeneratePrivateMembers(io::Printer* p) const = 0;
Expand Down Expand Up @@ -150,6 +160,32 @@ class FieldGeneratorBase {
<< descriptor_->cpp_type_name();
}

// Generates constexpr member initialization code, e.g.: `foo_{5}`.
// The default implementation generates the following code:
// - repeated fields and maps: `field_{}`
// - all other fields: `field_{<default value>}`
virtual void GenerateMemberConstexprConstructor(io::Printer* p) const;

// Generates member initialization code, e.g.: `foo_(5)`.
// The default implementation generates the following code:
// - repeated fields and maps: `field_{visibility, arena}`
// - split repeated fields (RawPtr): `field_{}`
// - all other fields: `field_{<default value>}`
virtual void GenerateMemberConstructor(io::Printer* p) const;

// Generates member copy initialization code, e.g.: `foo_(5)`.
// The default implementation generates the following code:
// - repeated fields and maps: `field_{visibility, arena, from.field_}`
// - all other fields: `field_{from.field_}`
virtual void GenerateMemberCopyConstructor(io::Printer* p) const;

// Generates 'placement new' copy construction code used to
// explicitly copy initialize oneof field values.
// The default implementation checks the current field to not be repeated,
// an extension or a map, and generates the following code:
// - `field_ = from.field_`
virtual void GenerateOneofCopyConstruct(io::Printer* p) const;

virtual void GenerateAggregateInitializer(io::Printer* p) const;

virtual void GenerateConstexprAggregateInitializer(io::Printer* p) const;
Expand All @@ -170,15 +206,16 @@ class FieldGeneratorBase {
}

protected:
// TODO(b/245791219): Remove these members and make this a pure interface.
const FieldDescriptor* descriptor_;
const Options& options_;
MessageSCCAnalyzer* scc_;
absl::flat_hash_map<absl::string_view, std::string> variables_;

private:
bool should_split_ = false;
bool is_trivial_ = false;
bool has_trivial_value_ = false;
bool has_trivial_zero_default_ = false;
bool is_message_ = false;
bool is_group_ = false;
bool is_string_ = false;
Expand All @@ -189,6 +226,7 @@ class FieldGeneratorBase {
bool is_weak_ = false;
bool is_oneof_ = false;
FieldOptions::CType string_type_ = FieldOptions::STRING;
bool has_default_constexpr_constructor_ = false;
};

inline FieldGeneratorBase::~FieldGeneratorBase() = default;
Expand Down Expand Up @@ -220,6 +258,27 @@ class FieldGenerator {
FieldGenerator(FieldGenerator&&) = default;
FieldGenerator& operator=(FieldGenerator&&) = default;

// Properties: see FieldGeneratorBase for documentation
bool should_split() const { return impl_->should_split(); }
bool is_trivial() const { return impl_->is_trivial(); }
bool has_trivial_value() const { return impl_->has_trivial_value(); }
bool has_trivial_zero_default() const {
return impl_->has_trivial_zero_default();
}
bool is_message() const { return impl_->is_message(); }
bool is_group() const { return impl_->is_group(); }
bool is_weak() const { return impl_->is_weak(); }
bool is_lazy() const { return impl_->is_lazy(); }
bool is_foreign() const { return impl_->is_foreign(); }
bool is_string() const { return impl_->is_string(); }
bool is_bytes() const { return impl_->is_bytes(); }
FieldOptions::CType string_type() const { return impl_->string_type(); }
bool is_oneof() const { return impl_->is_oneof(); }
bool is_inlined() const { return impl_->is_inlined(); }
bool has_default_constexpr_constructor() const {
return impl_->has_default_constexpr_constructor();
}

// Prints private members needed to represent this field.
//
// These are placed inside the class definition.
Expand All @@ -236,6 +295,26 @@ class FieldGenerator {
impl_->GenerateStaticMembers(p);
}

void GenerateMemberConstructor(io::Printer* p) const {
auto vars = PushVarsForCall(p);
impl_->GenerateMemberConstructor(p);
}

void GenerateMemberCopyConstructor(io::Printer* p) const {
auto vars = PushVarsForCall(p);
impl_->GenerateMemberCopyConstructor(p);
}

void GenerateOneofCopyConstruct(io::Printer* p) const {
auto vars = PushVarsForCall(p);
impl_->GenerateOneofCopyConstruct(p);
}

void GenerateMemberConstexprConstructor(io::Printer* p) const {
auto vars = PushVarsForCall(p);
impl_->GenerateMemberConstexprConstructor(p);
}

// Generates declarations for all of the accessor functions related to this
// field.
//
Expand Down
37 changes: 36 additions & 1 deletion src/google/protobuf/compiler/cpp/field_generators/cord_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void SetCordVariables(
(*variables)["default_variable_field"] = MakeDefaultFieldName(descriptor);
(*variables)["default_variable"] =
descriptor->default_value_string().empty()
? absl::StrCat(ProtobufNamespace(options),
? absl::StrCat("::", ProtobufNamespace(options),
"::internal::GetEmptyCordAlreadyInited()")
: absl::StrCat(
QualifiedClassName(descriptor->containing_type(), options),
Expand All @@ -88,7 +88,9 @@ class CordFieldGenerator : public FieldGeneratorBase {
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
#ifndef PROTOBUF_EXPLICIT_CONSTRUCTORS
void GenerateDestructorCode(io::Printer* printer) const override;
#endif // !PROTOBUF_EXPLICIT_CONSTRUCTORS
void GenerateArenaDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
Expand All @@ -99,6 +101,37 @@ class CordFieldGenerator : public FieldGeneratorBase {
ArenaDtorNeeds NeedsArenaDestructor() const override {
return ArenaDtorNeeds::kRequired;
}

void GenerateMemberConstexprConstructor(io::Printer* p) const override {
if (descriptor_->default_value_string().empty()) {
p->Emit("$name$_{}");
} else {
p->Emit({{"Split", ShouldSplit(descriptor_, options_) ? "Split::" : ""}},
"$name$_{::absl::strings_internal::MakeStringConstant("
"$classname$::Impl_::$Split$_default_$name$_func_{})}");
}
}

void GenerateMemberConstructor(io::Printer* p) const override {
auto vars = p->WithVars(variables_);
if (descriptor_->default_value_string().empty()) {
p->Emit("$name$_{}");
} else {
p->Emit("$name$_{::absl::string_view($default$, $default_length$)}");
}
}

void GenerateMemberCopyConstructor(io::Printer* p) const override {
auto vars = p->WithVars(variables_);
p->Emit("$name$_{from.$name$_}");
}

void GenerateOneofCopyConstruct(io::Printer* p) const override {
auto vars = p->WithVars(variables_);
p->Emit(R"cc(
$field$ = ::$proto_ns$::Arena::Create<absl::Cord>(arena, *from.$field$);
)cc");
}
};

class CordOneofFieldGenerator : public CordFieldGenerator {
Expand Down Expand Up @@ -224,6 +257,7 @@ void CordFieldGenerator::GenerateConstructorCode(io::Printer* printer) const {
}
}

#ifndef PROTOBUF_EXPLICIT_CONSTRUCTORS
void CordFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (should_split()) {
Expand All @@ -233,6 +267,7 @@ void CordFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
}
format("$field$.~Cord();\n");
}
#endif // !PROTOBUF_EXPLICIT_CONSTRUCTORS

void CordFieldGenerator::GenerateArenaDestructorCode(
io::Printer* printer) const {
Expand Down
28 changes: 28 additions & 0 deletions src/google/protobuf/compiler/cpp/field_generators/enum_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <vector>

#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/memory/memory.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/cpp/field.h"
Expand Down Expand Up @@ -304,9 +305,11 @@ class RepeatedEnum : public FieldGeneratorBase {
$field_$.DeleteIfNotDefault();
)cc");
} else {
#ifndef PROTOBUF_EXPLICIT_CONSTRUCTORS
p->Emit(R"cc(
_internal_mutable_$name$()->~RepeatedField();
)cc");
#endif // !PROTOBUF_EXPLICIT_CONSTRUCTORS
}
}

Expand Down Expand Up @@ -346,6 +349,31 @@ class RepeatedEnum : public FieldGeneratorBase {
}
}

void GenerateMemberConstexprConstructor(io::Printer* p) const override {
p->Emit("$name$_{}");
if (has_cached_size_) {
p->Emit(",\n_$name$_cached_byte_size_{0}");
}
}

void GenerateMemberConstructor(io::Printer* p) const override {
p->Emit("$name$_{visibility, arena}");
if (has_cached_size_) {
p->Emit(",\n_$name$_cached_byte_size_{0}");
}
}

void GenerateMemberCopyConstructor(io::Printer* p) const override {
p->Emit("$name$_{visibility, arena, from.$name$_}");
if (has_cached_size_) {
p->Emit(",\n_$name$_cached_byte_size_{0}");
}
}

void GenerateOneofCopyConstruct(io::Printer* p) const override {
ABSL_LOG(FATAL) << "Not supported";
}

void GenerateCopyConstructorCode(io::Printer* p) const override {
if (should_split()) {
p->Emit(R"cc(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,11 @@ class Map : public FieldGeneratorBase {
)cc");
return;
}

#ifndef PROTOBUF_EXPLICIT_CONSTRUCTORS
p->Emit(R"cc(
$field_$.~$MapField$();
)cc");
#endif // !PROTOBUF_EXPLICIT_CONSTRUCTORS
}

void GeneratePrivateMembers(io::Printer* p) const override;
Expand Down
Loading

0 comments on commit a2b3470

Please sign in to comment.