Optimize `RepeatedPtrField`'s swap.

PiperOrigin-RevId: 553117489
pull/13438/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 85a81cc70d
commit 421ce84e5a
  1. 44
      src/google/protobuf/repeated_field.h
  2. 1
      src/google/protobuf/repeated_field_unittest.cc
  3. 23
      src/google/protobuf/repeated_ptr_field.h

@ -103,42 +103,6 @@ constexpr int RepeatedFieldLowerClampLimit() {
constexpr int kRepeatedFieldUpperClampLimit =
(std::numeric_limits<int>::max() / 2) + 1;
// Swaps two blocks of memory of size kSize:
template <size_t kSize>
void memswap(char* a, char* b) {
#if __SIZEOF_INT128__
using Buffer = __uint128_t;
#else
using Buffer = uint64_t;
#endif
constexpr size_t kBlockSize = sizeof(Buffer);
Buffer buf;
for (size_t i = 0; i < kSize / kBlockSize; ++i) {
memcpy(&buf, a, kBlockSize);
memcpy(a, b, kBlockSize);
memcpy(b, &buf, kBlockSize);
a += kBlockSize;
b += kBlockSize;
}
#if defined(__GNUC__) && !defined(__clang__)
// Workaround GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif // __GNUC__
// Swap the leftover bytes, could be zero.
memcpy(&buf, a, kSize % kBlockSize);
memcpy(a, b, kSize % kBlockSize);
memcpy(b, &buf, kSize % kBlockSize);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif // GCC
}
template <typename Element>
class RepeatedIterator;
@ -880,11 +844,11 @@ inline void RepeatedField<Element>::InternalSwap(RepeatedField* other) {
// Swap all fields at once.
static_assert(std::is_standard_layout<RepeatedField<Element>>::value,
"offsetof() requires standard layout before c++17");
static constexpr size_t kOffset = offsetof(RepeatedField, current_size_);
internal::memswap<offsetof(RepeatedField, arena_or_elements_) +
sizeof(this->arena_or_elements_) -
offsetof(RepeatedField, current_size_)>(
reinterpret_cast<char*>(this) + offsetof(RepeatedField, current_size_),
reinterpret_cast<char*>(other) + offsetof(RepeatedField, current_size_));
sizeof(this->arena_or_elements_) - kOffset>(
reinterpret_cast<char*>(this) + kOffset,
reinterpret_cast<char*>(other) + kOffset);
}
template <typename Element>

@ -41,6 +41,7 @@
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>
#include <list>

@ -44,17 +44,13 @@
#ifndef GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
#define GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
#include <utility>
#ifdef _MSC_VER
// This is required for min/max on VS2013 only.
#include <algorithm>
#endif
#include <cstddef>
#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
#include "google/protobuf/arena.h"
#include "google/protobuf/port.h"
@ -98,6 +94,14 @@ class RepeatedPtrOverPtrsIterator;
namespace internal {
// Swaps two non-overlapping blocks of memory of size `N`
template <size_t N>
inline void memswap(char* PROTOBUF_RESTRICT a, char* PROTOBUF_RESTRICT b) {
// `PROTOBUF_RESTRICT` tells compiler that blocks do not overlapping which
// allows it to genererate optimized code for swap_ranges.
std::swap_ranges(a, a + N, b);
}
// type-traits helper for RepeatedPtrFieldBase: we only want to invoke
// arena-related "copy if on different arena" behavior if the necessary methods
// exist on the contained type. In particular, we rely on MergeFrom() existing
@ -308,11 +312,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
ABSL_DCHECK(this != rhs);
// Swap all fields at once.
auto temp = std::make_tuple(rhs->arena_, rhs->current_size_,
rhs->total_size_, rhs->rep_);
std::tie(rhs->arena_, rhs->current_size_, rhs->total_size_, rhs->rep_) =
std::make_tuple(arena_, current_size_, total_size_, rep_);
std::tie(arena_, current_size_, total_size_, rep_) = temp;
internal::memswap<sizeof(RepeatedPtrFieldBase)>(
reinterpret_cast<char*>(this), reinterpret_cast<char*>(rhs));
}
// Prepares the container for adding elements via `AddAllocatedForParse`.

Loading…
Cancel
Save