From 9b019ee2711fdeb29917bfb8c4c1292635c35aa7 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Thu, 5 Sep 2024 11:57:05 -0700 Subject: [PATCH] Add prefetching of subsequent extensions in ExtensionSet::ForEach. PiperOrigin-RevId: 671457336 --- src/google/protobuf/extension_set.cc | 63 +++++-- src/google/protobuf/extension_set.h | 96 +++++++++-- src/google/protobuf/extension_set_heavy.cc | 53 +++--- src/google/protobuf/reflection_visit_fields.h | 163 +++++++++--------- 4 files changed, 244 insertions(+), 131 deletions(-) diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc index c5af8e4146476..b839efa9e6907 100644 --- a/src/google/protobuf/extension_set.cc +++ b/src/google/protobuf/extension_set.cc @@ -186,7 +186,8 @@ void ExtensionSet::RegisterMessageExtension(const MessageLite* extendee, ExtensionSet::~ExtensionSet() { // Deletes all allocated extensions. if (arena_ == nullptr) { - ForEach([](int /* number */, Extension& ext) { ext.Free(); }); + ForEach([](int /* number */, Extension& ext) { ext.Free(); }, + PrefetchNta{}); if (PROTOBUF_PREDICT_FALSE(is_large())) { delete map_.large; } else { @@ -225,7 +226,7 @@ bool ExtensionSet::HasLazy(int number) const { int ExtensionSet::NumExtensions() const { int result = 0; - ForEach([&result](int /* number */, const Extension& ext) { + ForEachNoPrefetch([&result](int /* number */, const Extension& ext) { if (!ext.is_cleared) { ++result; } @@ -308,6 +309,7 @@ enum { REPEATED_FIELD, OPTIONAL_FIELD }; ABSL_DCHECK_EQ(cpp_type(extension->type), \ WireFormatLite::CPPTYPE_##UPPERCASE); \ extension->is_repeated = false; \ + extension->is_pointer = false; \ } else { \ ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, UPPERCASE); \ } \ @@ -351,6 +353,7 @@ enum { REPEATED_FIELD, OPTIONAL_FIELD }; ABSL_DCHECK_EQ(cpp_type(extension->type), \ WireFormatLite::CPPTYPE_##UPPERCASE); \ extension->is_repeated = true; \ + extension->is_pointer = true; \ extension->is_packed = packed; \ extension->ptr.repeated_##LOWERCASE##_value = \ Arena::Create>(arena_); \ @@ -391,6 +394,7 @@ void* ExtensionSet::MutableRawRepeatedField(int number, FieldType field_type, // extension. if (MaybeNewExtension(number, desc, &extension)) { extension->is_repeated = true; + extension->is_pointer = true; extension->type = field_type; extension->is_packed = packed; @@ -487,6 +491,7 @@ void ExtensionSet::SetEnum(int number, FieldType type, int value, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_ENUM); extension->is_repeated = false; + extension->is_pointer = false; } else { ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, ENUM); } @@ -522,6 +527,7 @@ void ExtensionSet::AddEnum(int number, FieldType type, bool packed, int value, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_ENUM); extension->is_repeated = true; + extension->is_pointer = true; extension->is_packed = packed; extension->ptr.repeated_enum_value = Arena::Create>(arena_); @@ -554,6 +560,7 @@ std::string* ExtensionSet::MutableString(int number, FieldType type, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING); extension->is_repeated = false; + extension->is_pointer = true; extension->ptr.string_value = Arena::Create(arena_); } else { ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING); @@ -584,6 +591,7 @@ std::string* ExtensionSet::AddString(int number, FieldType type, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING); extension->is_repeated = true; + extension->is_pointer = true; extension->is_packed = false; extension->ptr.repeated_string_value = Arena::Create>(arena_); @@ -626,6 +634,7 @@ MessageLite* ExtensionSet::MutableMessage(int number, FieldType type, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = false; + extension->is_pointer = true; extension->is_lazy = false; extension->ptr.message_value = prototype.New(arena_); extension->is_cleared = false; @@ -663,6 +672,7 @@ void ExtensionSet::SetAllocatedMessage(int number, FieldType type, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = false; + extension->is_pointer = true; extension->is_lazy = false; if (message_arena == arena) { extension->ptr.message_value = message; @@ -707,6 +717,7 @@ void ExtensionSet::UnsafeArenaSetAllocatedMessage( extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = false; + extension->is_pointer = true; extension->is_lazy = false; extension->ptr.message_value = message; } else { @@ -805,6 +816,7 @@ MessageLite* ExtensionSet::AddMessage(int number, FieldType type, extension->type = type; ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = true; + extension->is_pointer = true; extension->ptr.repeated_message_value = Arena::Create>(arena_); } else { @@ -920,7 +932,7 @@ void ExtensionSet::SwapElements(int number, int index1, int index2) { // =================================================================== void ExtensionSet::Clear() { - ForEach([](int /* number */, Extension& ext) { ext.Clear(); }); + ForEach([](int /* number */, Extension& ext) { ext.Clear(); }, Prefetch{}); } namespace { @@ -969,9 +981,11 @@ void ExtensionSet::MergeFrom(const MessageLite* extendee, other.map_.large->end())); } } - other.ForEach([extendee, this, &other](int number, const Extension& ext) { - this->InternalExtensionMergeFrom(extendee, number, ext, other.arena_); - }); + other.ForEach( + [extendee, this, &other](int number, const Extension& ext) { + this->InternalExtensionMergeFrom(extendee, number, ext, other.arena_); + }, + Prefetch{}); } void ExtensionSet::InternalExtensionMergeFrom(const MessageLite* extendee, @@ -987,6 +1001,7 @@ void ExtensionSet::InternalExtensionMergeFrom(const MessageLite* extendee, extension->type = other_extension.type; extension->is_packed = other_extension.is_packed; extension->is_repeated = true; + extension->is_pointer = true; } else { ABSL_DCHECK_EQ(extension->type, other_extension.type); ABSL_DCHECK_EQ(extension->is_packed, other_extension.is_packed); @@ -1049,6 +1064,7 @@ void ExtensionSet::InternalExtensionMergeFrom(const MessageLite* extendee, extension->type = other_extension.type; extension->is_packed = other_extension.is_packed; extension->is_repeated = false; + extension->is_pointer = true; if (other_extension.is_lazy) { extension->is_lazy = true; extension->ptr.lazymessage_value = @@ -1226,6 +1242,13 @@ const char* ExtensionSet::ParseMessageSetItem( metadata, ctx); } +bool ExtensionSet::FieldTypeIsPointer(FieldType type) { + return type == WireFormatLite::TYPE_STRING || + type == WireFormatLite::TYPE_BYTES || + type == WireFormatLite::TYPE_GROUP || + type == WireFormatLite::TYPE_MESSAGE; +} + uint8_t* ExtensionSet::_InternalSerializeImpl( const MessageLite* extendee, int start_field_number, int end_field_number, uint8_t* target, io::EpsCopyOutputStream* stream) const { @@ -1252,19 +1275,23 @@ uint8_t* ExtensionSet::InternalSerializeMessageSetWithCachedSizesToArray( const MessageLite* extendee, uint8_t* target, io::EpsCopyOutputStream* stream) const { const ExtensionSet* extension_set = this; - ForEach([&target, extendee, stream, extension_set](int number, - const Extension& ext) { - target = ext.InternalSerializeMessageSetItemWithCachedSizesToArray( - extendee, extension_set, number, target, stream); - }); + ForEach( + [&target, extendee, stream, extension_set](int number, + const Extension& ext) { + target = ext.InternalSerializeMessageSetItemWithCachedSizesToArray( + extendee, extension_set, number, target, stream); + }, + Prefetch{}); return target; } size_t ExtensionSet::ByteSize() const { size_t total_size = 0; - ForEach([&total_size](int number, const Extension& ext) { - total_size += ext.ByteSize(number); - }); + ForEach( + [&total_size](int number, const Extension& ext) { + total_size += ext.ByteSize(number); + }, + Prefetch{}); return total_size; } @@ -1932,9 +1959,11 @@ size_t ExtensionSet::Extension::MessageSetItemByteSize(int number) const { size_t ExtensionSet::MessageSetByteSize() const { size_t total_size = 0; - ForEach([&total_size](int number, const Extension& ext) { - total_size += ext.MessageSetItemByteSize(number); - }); + ForEach( + [&total_size](int number, const Extension& ext) { + total_size += ext.MessageSetItemByteSize(number); + }, + Prefetch{}); return total_size; } diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h index aaa9af89b2ad1..5a8d9abff302f 100644 --- a/src/google/protobuf/extension_set.h +++ b/src/google/protobuf/extension_set.h @@ -28,6 +28,8 @@ #include "google/protobuf/stubs/common.h" #include "absl/base/call_once.h" +#include "absl/base/casts.h" +#include "absl/base/prefetch.h" #include "absl/container/btree_map.h" #include "absl/log/absl_check.h" #include "google/protobuf/internal_visibility.h" @@ -555,6 +557,8 @@ class PROTOBUF_EXPORT ExtensionSet { friend void internal::InitializeLazyExtensionSet(); + static bool FieldTypeIsPointer(FieldType type); + const int32_t& GetRefInt32(int number, const int32_t& default_value) const; const int64_t& GetRefInt64(int number, const int64_t& default_value) const; const uint32_t& GetRefUInt32(int number, const uint32_t& default_value) const; @@ -670,6 +674,12 @@ class PROTOBUF_EXPORT ExtensionSet { size_t SpaceUsedExcludingSelfLong() const; bool IsInitialized(const ExtensionSet* ext_set, const MessageLite* extendee, int number, Arena* arena) const; + const void* PrefetchPtr() const { + ABSL_DCHECK_EQ(is_pointer, is_repeated || FieldTypeIsPointer(type)); + // We don't want to prefetch invalid/null pointers so if there isn't a + // pointer to prefetch, then return `this`. + return is_pointer ? absl::bit_cast(ptr) : this; + } // The order of these fields packs Extension into 24 bytes when using 8 // byte alignment. Consider this when adding or removing fields here. @@ -708,20 +718,23 @@ class PROTOBUF_EXPORT ExtensionSet { FieldType type; bool is_repeated; + // Whether the extension is a pointer. This is used for prefetching. + bool is_pointer : 1; + // For singular types, indicates if the extension is "cleared". This // happens when an extension is set and then later cleared by the caller. // We want to keep the Extension object around for reuse, so instead of // removing it from the map, we just set is_cleared = true. This has no // meaning for repeated types; for those, the size of the RepeatedField // simply becomes zero when cleared. - bool is_cleared : 4; + bool is_cleared : 1; // For singular message types, indicates whether lazy parsing is enabled // for this extension. This field is only valid when type == TYPE_MESSAGE // and !is_repeated because we only support lazy parsing for singular // message types currently. If is_lazy = true, the extension is stored in // lazymessage_value. Otherwise, the extension will be message_value. - bool is_lazy : 4; + bool is_lazy : 1; // For repeated types, this indicates if the [packed=true] option is set. bool is_packed; @@ -779,32 +792,93 @@ class PROTOBUF_EXPORT ExtensionSet { return PROTOBUF_PREDICT_FALSE(is_large()) ? map_.large->size() : flat_size_; } + // For use as `PrefetchFunctor`s in `ForEach`. + struct Prefetch { + void operator()(const void* ptr) const { absl::PrefetchToLocalCache(ptr); } + }; + struct PrefetchNta { + void operator()(const void* ptr) const { + absl::PrefetchToLocalCacheNta(ptr); + } + }; + + template + static KeyValueFunctor ForEachPrefetchImpl(Iterator it, Iterator end, + KeyValueFunctor func, + PrefetchFunctor prefetch_func) { + // Note: based on arena's ChunkList::Cleanup(). + // Prefetch distance 16 performs better than 8 in load tests. + constexpr int kPrefetchDistance = 16; + Iterator prefetch = it; + // Prefetch the first kPrefetchDistance extensions. + for (int i = 0; prefetch != end && i < kPrefetchDistance; ++prefetch, ++i) { + prefetch_func(prefetch->second.PrefetchPtr()); + } + // For the middle extensions, call func and then prefetch the extension + // kPrefetchDistance after the current one. + for (; prefetch != end; ++it, ++prefetch) { + func(it->first, it->second); + prefetch_func(prefetch->second.PrefetchPtr()); + } + // Call func on the rest without prefetching. + for (; it != end; ++it) func(it->first, it->second); + return std::move(func); + } + // Similar to std::for_each. // Each Iterator is decomposed into ->first and ->second fields, so // that the KeyValueFunctor can be agnostic vis-a-vis KeyValue-vs-std::pair. + // Applies a functor to the pairs in sorted order and + // prefetches ahead. + template + KeyValueFunctor ForEach(KeyValueFunctor func, PrefetchFunctor prefetch_func) { + if (PROTOBUF_PREDICT_FALSE(is_large())) { + return ForEachPrefetchImpl(map_.large->begin(), map_.large->end(), + std::move(func), std::move(prefetch_func)); + } + return ForEachPrefetchImpl(flat_begin(), flat_end(), std::move(func), + std::move(prefetch_func)); + } + // As above, but const. + template + KeyValueFunctor ForEach(KeyValueFunctor func, + PrefetchFunctor prefetch_func) const { + if (PROTOBUF_PREDICT_FALSE(is_large())) { + return ForEachPrefetchImpl(map_.large->begin(), map_.large->end(), + std::move(func), std::move(prefetch_func)); + } + return ForEachPrefetchImpl(flat_begin(), flat_end(), std::move(func), + std::move(prefetch_func)); + } + + // As above, but without prefetching. This is for use in cases where we never + // use the pointed-to extension values in `func`. template - static KeyValueFunctor ForEach(Iterator begin, Iterator end, - KeyValueFunctor func) { + static KeyValueFunctor ForEachNoPrefetch(Iterator begin, Iterator end, + KeyValueFunctor func) { for (Iterator it = begin; it != end; ++it) func(it->first, it->second); return std::move(func); } // Applies a functor to the pairs in sorted order. template - KeyValueFunctor ForEach(KeyValueFunctor func) { + KeyValueFunctor ForEachNoPrefetch(KeyValueFunctor func) { if (PROTOBUF_PREDICT_FALSE(is_large())) { - return ForEach(map_.large->begin(), map_.large->end(), std::move(func)); + return ForEachNoPrefetch(map_.large->begin(), map_.large->end(), + std::move(func)); } - return ForEach(flat_begin(), flat_end(), std::move(func)); + return ForEachNoPrefetch(flat_begin(), flat_end(), std::move(func)); } - // Applies a functor to the pairs in sorted order. + // As above, but const. template - KeyValueFunctor ForEach(KeyValueFunctor func) const { + KeyValueFunctor ForEachNoPrefetch(KeyValueFunctor func) const { if (PROTOBUF_PREDICT_FALSE(is_large())) { - return ForEach(map_.large->begin(), map_.large->end(), std::move(func)); + return ForEachNoPrefetch(map_.large->begin(), map_.large->end(), + std::move(func)); } - return ForEach(flat_begin(), flat_end(), std::move(func)); + return ForEachNoPrefetch(flat_begin(), flat_end(), std::move(func)); } // Merges existing Extension from other_extension diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc index 3023792e03fdc..134d08296a1e7 100644 --- a/src/google/protobuf/extension_set_heavy.cc +++ b/src/google/protobuf/extension_set_heavy.cc @@ -64,27 +64,30 @@ class DescriptorPoolExtensionFinder { void ExtensionSet::AppendToList( const Descriptor* extendee, const DescriptorPool* pool, std::vector* output) const { - ForEach([extendee, pool, &output](int number, const Extension& ext) { - bool has = false; - if (ext.is_repeated) { - has = ext.GetSize() > 0; - } else { - has = !ext.is_cleared; - } - - if (has) { - // TODO: Looking up each field by number is somewhat unfortunate. - // Is there a better way? The problem is that descriptors are lazily- - // initialized, so they might not even be constructed until - // AppendToList() is called. + ForEach( + [extendee, pool, &output](int number, const Extension& ext) { + bool has = false; + if (ext.is_repeated) { + has = ext.GetSize() > 0; + } else { + has = !ext.is_cleared; + } - if (ext.descriptor == nullptr) { - output->push_back(pool->FindExtensionByNumber(extendee, number)); - } else { - output->push_back(ext.descriptor); - } - } - }); + if (has) { + // TODO: Looking up each field by number is somewhat + // unfortunate. + // Is there a better way? The problem is that descriptors are + // lazily-initialized, so they might not even be constructed until + // AppendToList() is called. + + if (ext.descriptor == nullptr) { + output->push_back(pool->FindExtensionByNumber(extendee, number)); + } else { + output->push_back(ext.descriptor); + } + } + }, + Prefetch{}); } inline FieldDescriptor::Type real_type(FieldType type) { @@ -133,6 +136,7 @@ MessageLite* ExtensionSet::MutableMessage(const FieldDescriptor* descriptor, extension->type = descriptor->type(); ABSL_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE); extension->is_repeated = false; + extension->is_pointer = true; extension->is_packed = false; const MessageLite* prototype = factory->GetPrototype(descriptor->message_type()); @@ -210,6 +214,7 @@ ExtensionSet::Extension* ExtensionSet::MaybeNewRepeatedExtension( extension->type = descriptor->type(); ABSL_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE); extension->is_repeated = true; + extension->is_pointer = true; extension->ptr.repeated_message_value = Arena::Create >(arena_); } else { @@ -353,9 +358,11 @@ int ExtensionSet::SpaceUsedExcludingSelf() const { size_t ExtensionSet::SpaceUsedExcludingSelfLong() const { size_t total_size = (is_large() ? map_.large->size() : flat_capacity_) * sizeof(KeyValue); - ForEach([&total_size](int /* number */, const Extension& ext) { - total_size += ext.SpaceUsedExcludingSelfLong(); - }); + ForEach( + [&total_size](int /* number */, const Extension& ext) { + total_size += ext.SpaceUsedExcludingSelfLong(); + }, + Prefetch{}); return total_size; } diff --git a/src/google/protobuf/reflection_visit_fields.h b/src/google/protobuf/reflection_visit_fields.h index 3b53155b51262..063486dc2caad 100644 --- a/src/google/protobuf/reflection_visit_fields.h +++ b/src/google/protobuf/reflection_visit_fields.h @@ -303,102 +303,105 @@ void ReflectionVisit::VisitFields(MessageT& message, CallbackFn&& func, auto* extendee = reflection->descriptor_; auto* pool = reflection->descriptor_pool_; - set.ForEach([&](int number, auto& ext) { - ABSL_DCHECK_GT(ext.type, 0); - ABSL_DCHECK_LE(ext.type, FieldDescriptor::MAX_TYPE); - - if (!ShouldVisit(mask, FieldDescriptor::TypeToCppType( - static_cast(ext.type)))) { - return; - } + set.ForEach( + [&](int number, auto& ext) { + ABSL_DCHECK_GT(ext.type, 0); + ABSL_DCHECK_LE(ext.type, FieldDescriptor::MAX_TYPE); + + if (!ShouldVisit(mask, + FieldDescriptor::TypeToCppType( + static_cast(ext.type)))) { + return; + } - if (ext.is_repeated) { - if (ext.GetSize() == 0) return; + if (ext.is_repeated) { + if (ext.GetSize() == 0) return; - switch (ext.type) { + switch (ext.type) { #define PROTOBUF_HANDLE_CASE(TYPE, NAME) \ case FieldDescriptor::TYPE_##TYPE: \ func(internal::Repeated##NAME##DynamicExtensionInfo{ \ ext, number}); \ break; - PROTOBUF_HANDLE_CASE(DOUBLE, Double); - PROTOBUF_HANDLE_CASE(FLOAT, Float); - PROTOBUF_HANDLE_CASE(INT64, Int64); - PROTOBUF_HANDLE_CASE(UINT64, UInt64); - PROTOBUF_HANDLE_CASE(INT32, Int32); - PROTOBUF_HANDLE_CASE(FIXED64, Fixed64); - PROTOBUF_HANDLE_CASE(FIXED32, Fixed32); - PROTOBUF_HANDLE_CASE(BOOL, Bool); - PROTOBUF_HANDLE_CASE(UINT32, UInt32); - PROTOBUF_HANDLE_CASE(ENUM, Enum); - PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32); - PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64); - PROTOBUF_HANDLE_CASE(SINT32, SInt32); - PROTOBUF_HANDLE_CASE(SINT64, SInt64); - - PROTOBUF_HANDLE_CASE(MESSAGE, Message); - PROTOBUF_HANDLE_CASE(GROUP, Group); - - case FieldDescriptor::TYPE_BYTES: - case FieldDescriptor::TYPE_STRING: - func(internal::RepeatedStringDynamicExtensionInfo{ - ext, number}); - break; - default: - internal::Unreachable(); - break; + PROTOBUF_HANDLE_CASE(DOUBLE, Double); + PROTOBUF_HANDLE_CASE(FLOAT, Float); + PROTOBUF_HANDLE_CASE(INT64, Int64); + PROTOBUF_HANDLE_CASE(UINT64, UInt64); + PROTOBUF_HANDLE_CASE(INT32, Int32); + PROTOBUF_HANDLE_CASE(FIXED64, Fixed64); + PROTOBUF_HANDLE_CASE(FIXED32, Fixed32); + PROTOBUF_HANDLE_CASE(BOOL, Bool); + PROTOBUF_HANDLE_CASE(UINT32, UInt32); + PROTOBUF_HANDLE_CASE(ENUM, Enum); + PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32); + PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64); + PROTOBUF_HANDLE_CASE(SINT32, SInt32); + PROTOBUF_HANDLE_CASE(SINT64, SInt64); + + PROTOBUF_HANDLE_CASE(MESSAGE, Message); + PROTOBUF_HANDLE_CASE(GROUP, Group); + + case FieldDescriptor::TYPE_BYTES: + case FieldDescriptor::TYPE_STRING: + func(internal::RepeatedStringDynamicExtensionInfo{ + ext, number}); + break; + default: + internal::Unreachable(); + break; #undef PROTOBUF_HANDLE_CASE - } - } else { - if (ext.is_cleared) return; + } + } else { + if (ext.is_cleared) return; - switch (ext.type) { + switch (ext.type) { #define PROTOBUF_HANDLE_CASE(TYPE, NAME) \ case FieldDescriptor::TYPE_##TYPE: \ func(internal::NAME##DynamicExtensionInfo{ext, number}); \ break; - PROTOBUF_HANDLE_CASE(DOUBLE, Double); - PROTOBUF_HANDLE_CASE(FLOAT, Float); - PROTOBUF_HANDLE_CASE(INT64, Int64); - PROTOBUF_HANDLE_CASE(UINT64, UInt64); - PROTOBUF_HANDLE_CASE(INT32, Int32); - PROTOBUF_HANDLE_CASE(FIXED64, Fixed64); - PROTOBUF_HANDLE_CASE(FIXED32, Fixed32); - PROTOBUF_HANDLE_CASE(BOOL, Bool); - PROTOBUF_HANDLE_CASE(UINT32, UInt32); - PROTOBUF_HANDLE_CASE(ENUM, Enum); - PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32); - PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64); - PROTOBUF_HANDLE_CASE(SINT32, SInt32); - PROTOBUF_HANDLE_CASE(SINT64, SInt64); - - PROTOBUF_HANDLE_CASE(GROUP, Group); - case FieldDescriptor::TYPE_MESSAGE: { - const FieldDescriptor* field = - ext.descriptor != nullptr - ? ext.descriptor - : pool->FindExtensionByNumber(extendee, number); - ABSL_DCHECK_EQ(field->number(), number); - bool is_mset = - field->containing_type()->options().message_set_wire_format(); - func(internal::MessageDynamicExtensionInfo{ext, number, - is_mset}); - break; - } + PROTOBUF_HANDLE_CASE(DOUBLE, Double); + PROTOBUF_HANDLE_CASE(FLOAT, Float); + PROTOBUF_HANDLE_CASE(INT64, Int64); + PROTOBUF_HANDLE_CASE(UINT64, UInt64); + PROTOBUF_HANDLE_CASE(INT32, Int32); + PROTOBUF_HANDLE_CASE(FIXED64, Fixed64); + PROTOBUF_HANDLE_CASE(FIXED32, Fixed32); + PROTOBUF_HANDLE_CASE(BOOL, Bool); + PROTOBUF_HANDLE_CASE(UINT32, UInt32); + PROTOBUF_HANDLE_CASE(ENUM, Enum); + PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32); + PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64); + PROTOBUF_HANDLE_CASE(SINT32, SInt32); + PROTOBUF_HANDLE_CASE(SINT64, SInt64); + + PROTOBUF_HANDLE_CASE(GROUP, Group); + case FieldDescriptor::TYPE_MESSAGE: { + const FieldDescriptor* field = + ext.descriptor != nullptr + ? ext.descriptor + : pool->FindExtensionByNumber(extendee, number); + ABSL_DCHECK_EQ(field->number(), number); + bool is_mset = + field->containing_type()->options().message_set_wire_format(); + func(internal::MessageDynamicExtensionInfo{ + ext, number, is_mset}); + break; + } - case FieldDescriptor::TYPE_BYTES: - case FieldDescriptor::TYPE_STRING: - func( - internal::StringDynamicExtensionInfo{ext, number}); - break; + case FieldDescriptor::TYPE_BYTES: + case FieldDescriptor::TYPE_STRING: + func(internal::StringDynamicExtensionInfo{ext, + number}); + break; - default: - internal::Unreachable(); - break; + default: + internal::Unreachable(); + break; #undef PROTOBUF_HANDLE_CASE - } - } - }); + } + } + }, + ExtensionSet::Prefetch{}); } template