19
19
#include < iterator>
20
20
#include < limits>
21
21
#include < memory>
22
+ #include < optional>
22
23
#include < string>
23
24
#include < utility>
24
25
#include < vector>
25
26
27
+ #include " absl/algorithm/container.h"
26
28
#include " absl/container/flat_hash_map.h"
27
29
#include " absl/container/flat_hash_set.h"
28
30
#include " absl/functional/any_invocable.h"
@@ -5010,6 +5012,34 @@ std::vector<uint32_t> MessageGenerator::RequiredFieldsBitMask() const {
5010
5012
return masks;
5011
5013
}
5012
5014
5015
+ static std::optional<int > FixedSize (const FieldDescriptor* field) {
5016
+ if (field->is_repeated () || field->real_containing_oneof () ||
5017
+ !field->has_presence ()) {
5018
+ return std::nullopt;
5019
+ }
5020
+
5021
+ const size_t tag_size = WireFormat::TagSize (field->number (), field->type ());
5022
+
5023
+ switch (field->type ()) {
5024
+ case FieldDescriptor::TYPE_FIXED32:
5025
+ return tag_size + WireFormatLite::kFixed32Size ;
5026
+ case FieldDescriptor::TYPE_FIXED64:
5027
+ return tag_size + WireFormatLite::kFixed64Size ;
5028
+ case FieldDescriptor::TYPE_SFIXED32:
5029
+ return tag_size + WireFormatLite::kSFixed32Size ;
5030
+ case FieldDescriptor::TYPE_SFIXED64:
5031
+ return tag_size + WireFormatLite::kSFixed64Size ;
5032
+ case FieldDescriptor::TYPE_FLOAT:
5033
+ return tag_size + WireFormatLite::kFloatSize ;
5034
+ case FieldDescriptor::TYPE_DOUBLE:
5035
+ return tag_size + WireFormatLite::kDoubleSize ;
5036
+ case FieldDescriptor::TYPE_BOOL:
5037
+ return tag_size + WireFormatLite::kBoolSize ;
5038
+ default :
5039
+ return std::nullopt;
5040
+ }
5041
+ }
5042
+
5013
5043
void MessageGenerator::GenerateByteSize (io::Printer* p) {
5014
5044
if (HasSimpleBaseClass (descriptor_, options_)) return ;
5015
5045
@@ -5039,14 +5069,45 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
5039
5069
return ;
5040
5070
}
5041
5071
5042
- std::vector<FieldChunk> chunks = CollectFields (
5043
- optimized_order_, options_,
5044
- [&](const FieldDescriptor* a, const FieldDescriptor* b) -> bool {
5072
+ std::vector<const FieldDescriptor*> fixed;
5073
+ std::vector<const FieldDescriptor*> rest;
5074
+ for (auto * f : optimized_order_) {
5075
+ if (FixedSize (f).has_value ()) {
5076
+ fixed.push_back (f);
5077
+ } else {
5078
+ rest.push_back (f);
5079
+ }
5080
+ }
5081
+
5082
+ // Sort the fixed fields to ensure maximum grouping.
5083
+ // The layout of the fields is irrelevant because we are not going to read
5084
+ // them. We only look at the hasbits.
5085
+ const auto fixed_tuple = [&](auto * f) {
5086
+ return std::make_tuple (HasWordIndex (f), FixedSize (f));
5087
+ };
5088
+ absl::c_sort (
5089
+ fixed, [&](auto * a, auto * b) { return fixed_tuple (a) < fixed_tuple (b); });
5090
+ std::vector<FieldChunk> fixed_chunks =
5091
+ CollectFields (fixed, options_, [&](const auto * a, const auto * b) {
5092
+ return fixed_tuple (a) == fixed_tuple (b);
5093
+ });
5094
+
5095
+ std::vector<FieldChunk> chunks =
5096
+ CollectFields (rest, options_, [&](const auto * a, const auto * b) {
5045
5097
return a->label () == b->label () && HasByteIndex (a) == HasByteIndex (b) &&
5046
5098
IsLikelyPresent (a, options_) == IsLikelyPresent (b, options_) &&
5047
5099
ShouldSplit (a, options_) == ShouldSplit (b, options_);
5048
5100
});
5049
5101
5102
+ // Interleave the fixed chunks in the right place to be able to reuse
5103
+ // cached_has_bits if available. Otherwise, add them to the end.
5104
+ for (auto & chunk : fixed_chunks) {
5105
+ auto it = std::find_if (chunks.begin (), chunks.end (), [&](auto & c) {
5106
+ return HasWordIndex (c.fields [0 ]) == HasWordIndex (chunk.fields [0 ]);
5107
+ });
5108
+ chunks.insert (it, std::move (chunk));
5109
+ }
5110
+
5050
5111
p->Emit (
5051
5112
{{" handle_extension_set" ,
5052
5113
[&] {
@@ -5086,6 +5147,15 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
5086
5147
auto it = chunks.begin ();
5087
5148
auto end = chunks.end ();
5088
5149
int cached_has_word_index = -1 ;
5150
+ const auto update_cached_has_bits = [&](auto & fields) {
5151
+ if (cached_has_word_index == HasWordIndex (fields.front ())) return ;
5152
+
5153
+ cached_has_word_index = HasWordIndex (fields.front ());
5154
+ p->Emit ({{" index" , cached_has_word_index}},
5155
+ R"cc(
5156
+ cached_has_bits = this_.$has_bits$[$index$];
5157
+ )cc" );
5158
+ };
5089
5159
5090
5160
while (it != end) {
5091
5161
auto next =
@@ -5096,6 +5166,25 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
5096
5166
5097
5167
while (it != next) {
5098
5168
const auto & fields = it->fields ;
5169
+
5170
+ // If the chunk is a fixed size singular chunk, use a branchless
5171
+ // approach for it.
5172
+ if (std::optional<int > fsize = FixedSize (fields[0 ])) {
5173
+ update_cached_has_bits (fields);
5174
+ uint32_t mask = GenChunkMask (fields, has_bit_indices_);
5175
+ p->Emit ({{" mask" , absl::StrFormat (" 0x%08xu" , mask)},
5176
+ {" popcount" , absl::has_single_bit (mask)
5177
+ ? " static_cast<bool>"
5178
+ : " ::absl::popcount" },
5179
+ {" fsize" , *fsize}},
5180
+ R"cc(
5181
+ //~
5182
+ total_size += $popcount$($mask$ & cached_has_bits) * $fsize$;
5183
+ )cc" );
5184
+ ++it;
5185
+ continue ;
5186
+ }
5187
+
5099
5188
const bool check_has_byte =
5100
5189
fields.size () > 1 && HasWordIndex (fields[0 ]) != kNoHasbit &&
5101
5190
!IsLikelyPresent (fields.back (), options_);
@@ -5112,14 +5201,7 @@ void MessageGenerator::GenerateByteSize(io::Printer* p) {
5112
5201
{" may_update_cached_has_word_index" ,
5113
5202
[&] {
5114
5203
if (!check_has_byte) return ;
5115
- if (cached_has_word_index == HasWordIndex (fields.front ()))
5116
- return ;
5117
-
5118
- cached_has_word_index = HasWordIndex (fields.front ());
5119
- p->Emit ({{" index" , cached_has_word_index}},
5120
- R"cc(
5121
- cached_has_bits = this_.$has_bits$[$index$];
5122
- )cc" );
5204
+ update_cached_has_bits (fields);
5123
5205
}},
5124
5206
{" check_if_chunk_present" ,
5125
5207
[&] {
0 commit comments