Skip to content

Commit

Permalink
Remove dedicated fast path for required fields in ByteSizeLong.
Browse files Browse the repository at this point in the history
Formerly, they had their own fast path.  Now they share the chunked processing of all fields.  This makes chunked processing more effective as an optimization and also eliminates replicated code for repeated fields.

PiperOrigin-RevId: 532224517
  • Loading branch information
fowles authored and Copybara-Service committed May 15, 2023
1 parent cfb702a commit 098f21e
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 110 deletions.
80 changes: 0 additions & 80 deletions src/google/protobuf/compiler/cpp/message.cc
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,6 @@ bool IsCrossFileMaybeMap(const FieldDescriptor* field) {
return IsCrossFileMessage(field);
}

bool IsRequired(const std::vector<const FieldDescriptor*>& v) {
return v.front()->is_required();
}

bool HasNonSplitOptionalString(const Descriptor* desc, const Options& options) {
for (const auto* field : FieldRange(desc)) {
if (IsString(field, options) && !field->is_repeated() &&
Expand Down Expand Up @@ -1690,14 +1686,6 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* p) {
oneof->name());
}

if (HasGeneratedMethods(descriptor_->file(), options_) &&
!descriptor_->options().message_set_wire_format() &&
num_required_fields_ > 1) {
format(
"// helper for ByteSizeLong()\n"
"::size_t RequiredFieldsByteSizeFallback() const;\n\n");
}

if (HasGeneratedMethods(descriptor_->file(), options_)) {
parse_function_generator_->GenerateDataDecls(p);
}
Expand Down Expand Up @@ -4075,34 +4063,6 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
}

Formatter format(p);
if (num_required_fields_ > 1) {
// Emit a function (rarely used, we hope) that handles the required fields
// by checking for each one individually.
format(
"::size_t $classname$::RequiredFieldsByteSizeFallback() const {\n"
"// @@protoc_insertion_point(required_fields_byte_size_fallback_start:"
"$full_name$)\n");
format.Indent();
format("::size_t total_size = 0;\n");
for (auto field : optimized_order_) {
if (field->is_required()) {
format("\n");
auto v = p->WithVars(HasBitVars(field));
format("if (($has_bits$[$has_array_index$] & $has_mask$) != 0) {\n");
format.Indent();
PrintFieldComment(format, field);
field_generators_.get(field).GenerateByteSize(p);
format.Outdent();
format("}\n");
}
}
format(
"\n"
"return total_size;\n");
format.Outdent();
format("}\n");
}

format(
"::size_t $classname$::ByteSizeLong() const {\n"
"$annotate_bytesize$"
Expand All @@ -4118,53 +4078,13 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
"\n");
}

// Handle required fields (if any). We expect all of them to be
// present, so emit one conditional that checks for that. If they are all
// present then the fast path executes; otherwise the slow path executes.
if (num_required_fields_ > 1) {
// The fast path works if all required fields are present.
const std::vector<uint32_t> masks_for_has_bits = RequiredFieldsBitMask();
format("if ($1$) { // All required fields are present.\n",
ConditionalToCheckBitmasks(masks_for_has_bits));
format.Indent();
// Oneof fields cannot be required, so optimized_order_ contains all of the
// fields that we need to potentially emit.
for (auto field : optimized_order_) {
if (!field->is_required()) continue;
PrintFieldComment(format, field);
field_generators_.get(field).GenerateByteSize(p);
format("\n");
}
format.Outdent();
format(
"} else {\n" // the slow path
" total_size += RequiredFieldsByteSizeFallback();\n"
"}\n");
} else {
// num_required_fields_ <= 1: no need to be tricky
for (auto field : optimized_order_) {
if (!field->is_required()) continue;
PrintFieldComment(format, field);
auto v = p->WithVars(HasBitVars(field));
format("if (($has_bits$[$has_array_index$] & $has_mask$) != 0) {\n");
format.Indent();
field_generators_.get(field).GenerateByteSize(p);
format.Outdent();
format("}\n");
}
}

std::vector<std::vector<const FieldDescriptor*>> chunks = CollectFields(
optimized_order_,
[&](const FieldDescriptor* a, const FieldDescriptor* b) -> bool {
return a->label() == b->label() && HasByteIndex(a) == HasByteIndex(b) &&
ShouldSplit(a, options_) == ShouldSplit(b, options_);
});

// Remove chunks with required fields.
chunks.erase(std::remove_if(chunks.begin(), chunks.end(), IsRequired),
chunks.end());

ColdChunkSkipper cold_skipper(descriptor_, options_, chunks, has_bit_indices_,
kColdRatio);
int cached_has_word_index = -1;
Expand Down
40 changes: 13 additions & 27 deletions src/google/protobuf/descriptor.pb.cc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/google/protobuf/descriptor.pb.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 098f21e

Please sign in to comment.