Skip to content

Commit

Permalink
Speed up node initialization in MpMap.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 703225930
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Dec 5, 2024
1 parent e8dc865 commit bfe46c5
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 22 deletions.
47 changes: 25 additions & 22 deletions src/google/protobuf/generated_message_tctable_lite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2826,36 +2826,39 @@ PROTOBUF_NOINLINE const char* TcParser::MpMap(PROTOBUF_TC_PARAM_DECL) {

while (true) {
NodeBase* node = map.AllocNode();
char* const node_end =
reinterpret_cast<char*>(node) + map.type_info().node_size;
void* const node_key = node->GetVoidKey();

// Due to node alignment we can guarantee that we have at least 8 writable
// bytes from the key position to the end of the node.
// We can initialize the first and last 8 bytes, which takes care of all the
// scalar value types. This makes the VisitXXX calls below faster because
// the switch is much smaller.
// Assert this in debug mode, just in case.
ABSL_DCHECK_GE(node_end - static_cast<char*>(node_key), sizeof(uint64_t));
memset(node_key, 0, sizeof(uint64_t));
memset(node_end - sizeof(uint64_t), 0, sizeof(uint64_t));

map.VisitKey( //
node, absl::Overload{
[&](std::string* str) {
Arena::CreateInArenaStorage(str, map.arena());
},
// Already initialized above. Do nothing here.
[](void*) {},
});

map.VisitKey(node, //
absl::Overload{
[&](std::string* str) {
Arena::CreateInArenaStorage(str, map.arena());
},
[&](void* scalar) {
// Due to node alignment we can guarantee that we have at
// least 8 writable bytes at the key position (as long as
// we do it before we initialize the value). We can
// unconditionally write here.
// Assert this in debug mode, just in case.
ABSL_DCHECK_GE(
reinterpret_cast<char*>(node) +
map.type_info().node_size -
reinterpret_cast<char*>(node->GetVoidKey()),
sizeof(uint64_t));
memset(node->GetVoidKey(), 0, sizeof(uint64_t));
},
});

map.VisitValue(
map.VisitValue( //
node, absl::Overload{
[&](std::string* str) {
Arena::CreateInArenaStorage(str, map.arena());
},
[&](MessageLite* msg) {
aux[1].table->class_data->PlacementNew(msg, map.arena());
},
[](auto* scalar) { memset(scalar, 0, sizeof(*scalar)); },
// Already initialized above. Do nothing here.
[](void*) {},
});

ptr = ctx->ParseLengthDelimitedInlined(ptr, [&](const char* ptr) {
Expand Down
13 changes: 13 additions & 0 deletions src/google/protobuf/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,19 @@ class Map : private internal::KeyMapBase<internal::KeyForBase<Key>> {
static_assert(
sizeof(Map) == sizeof(internal::UntypedMapBase),
"Map must not have any data members beyond what is in UntypedMapBase.");

// Check for MpMap optimizations.
if constexpr (std::is_scalar_v<key_type>) {
static_assert(sizeof(key_type) <= sizeof(uint64_t),
"Scalar must be <= than uint64_t");
}
if constexpr (std::is_scalar_v<mapped_type>) {
static_assert(sizeof(mapped_type) <= sizeof(uint64_t),
"Scalar must be <= than uint64_t");
}
static_assert(internal::kMaxMessageAlignment >= sizeof(uint64_t));
static_assert(sizeof(Node) - sizeof(internal::NodeBase) >= sizeof(uint64_t),
"We must have at least this bytes for MpMap initialization");
}

template <typename P>
Expand Down

0 comments on commit bfe46c5

Please sign in to comment.