Skip to content

Commit 47f5a90

Browse files
sbenzaquencopybara-github
authored andcommitted
Refactor how move/copy/assign is implemented for messages with trivially copyable members.
Improvements: - Use a single implementation in MessageLite on top of memcpy, which we can share on the submessages. This reduces binary size costs and potentially increases icache performance. - Move constructors no longer do default+swap, so they are faster. - Move assignment no longer swaps, so they are faster. - CopyFrom no longer does Clear+Merge, so it is faster. PiperOrigin-RevId: 840694169
1 parent 3839268 commit 47f5a90

File tree

10 files changed

+912
-607
lines changed

10 files changed

+912
-607
lines changed

editions/golden/compare_cpp_codegen_failure.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
const;
4747
void clear_int32_field() ;
4848
@@ @@
49-
49+
}
5050
// SimpleProto3
5151

5252
-// optional int32 int32_field = 1;

editions/golden/compare_cpp_codegen_failure.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.cc&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;&#x0A;#include &quot;third_party/protobuf/editions/golden/simple_proto3.pb.h&quot;&#x0A;&#x0A;#include &lt;algorithm&gt;&#x0A;#include &lt;type_traits&gt;&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_impl.h&quot;&#x0A;#include &quot;third_party/protobuf/internal_visibility.h&quot;&#x0A;#include &quot;third_party/protobuf/extension_set.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_util.h&quot;&#x0A;#include &quot;third_party/protobuf/wire_format_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/io/zero_copy_stream_impl_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/generated_message_table_impl.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;, with the difference:&#x0A;@@ @@&#x0A; ::_pbi::TcParser::GetTable&lt;::protobuf_editions_test::golden::SimpleProto3&gt;(), // to_prefetch&#x0A; #endif // PROTOBUF_PREFETCH_PARSE_TABLE&#x0A; }, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {::_pbi::TcParser::FastV32S1,&#x0A; {8, 0, 0,&#x0A; PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_)}},&#x0A; }}, {{&#x0A; 65535, 65535&#x0A; }}, {{&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; {PROTOBUF_FIELD_OFFSET(SimpleProto3, _impl_.int32_field_), _Internal::kHasBitsOffset + 0, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)},&#x0A; }},&#x0A; // no aux_entries&#x0A;@@ @@&#x0A; (void)cached_has_bits;&#x0A; &#x0A; cached_has_bits = this_._impl_._has_bits_[0];&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; if (CheckHasBit(cached_has_bits, 0x00000001U)) {&#x0A; target =&#x0A; ::proto2::internal::WireFormatLite::WriteInt32ToArrayWithField&lt;1&gt;(&#x0A;@@ @@&#x0A; (void)cached_has_bits;&#x0A; &#x0A; {&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; cached_has_bits = this_._impl_._has_bits_[0];&#x0A; if (CheckHasBit(cached_has_bits, 0x00000001U)) {&#x0A; total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(" type=""></failure>
66
</testcase>
77
<testcase name="third_party/protobuf/editions/golden/simple_proto3.pb.h" status="run" result="completed" classname="DiffTest">
8-
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.h&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;&#x0A;#ifndef third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;#define third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;&#x0A;#include &lt;limits&gt;&#x0A;#include &lt;string&gt;&#x0A;#include &lt;type_traits&gt;&#x0A;#include &lt;utility&gt;&#x0A;&#x0A;// clang-format off&#x0A;#include &quot;third_party/protobuf/runtime_version.h&quot;&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/arena.h&quot;&#x0A;#include &quot;third_party/protobuf/arenastring.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_decl.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/generated_message_table.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/generated_message_table_impl.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/batch_builder.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/batch_wire_format.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_util.h&quot;&#x0A;#include &quot;third_party/protobuf/metadata_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/message_lite.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;// clang-format on&#x0A;&#x0A;#endif // third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;, with the difference:&#x0A;@@ @@&#x0A; enum : int {&#x0A; kInt32FieldFieldNumber = 1,&#x0A; };&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; [[nodiscard]] bool has_int32_field()&#x0A; const;&#x0A; void clear_int32_field() ;&#x0A;@@ @@&#x0A; &#x0A; // SimpleProto3&#x0A; &#x0A;-// optional int32 int32_field = 1;&#x0A;+// int32 int32_field = 1;&#x0A; inline bool SimpleProto3::has_int32_field() const {&#x0A; bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000001U);&#x0A; return value;" type=""></failure>
8+
<failure message="Value of: third_party/protobuf/editions/golden/simple_proto3.pb.h&#x0A;Expected: &#x0A;// Generated by the protocol buffer compiler. DO NOT EDIT!&#x0A;// NO CHECKED-IN PROTOBUF GENCODE&#x0A;// source: third_party/protobuf/editions/golden/simple_proto3.proto&#x0A;&#x0A;#ifndef third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;#define third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;&#x0A;#include &lt;limits&gt;&#x0A;#include &lt;string&gt;&#x0A;#include &lt;type_traits&gt;&#x0A;#include &lt;utility&gt;&#x0A;&#x0A;// clang-format off&#x0A;#include &quot;third_party/protobuf/runtime_version.h&quot;&#x0A;#include &quot;third_party/protobuf/io/coded_stream.h&quot;&#x0A;#include &quot;third_party/protobuf/arena.h&quot;&#x0A;#include &quot;third_party/protobuf/arenastring.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_tctable_decl.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/generated_message_table.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/generated_message_table_impl.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/batch_builder.h&quot;&#x0A;#include &quot;third_party/protobuf/v2/batch_wire_format.h&quot;&#x0A;#include &quot;third_party/protobuf/generated_message_util.h&quot;&#x0A;#include &quot;third_party/protobuf/metadata_lite.h&quot;&#x0A;#include &quot;third_party/protobuf/message_lite.h&quot;&#x0A;// @@protoc_insertion_point(includes)&#x0A;&#x0A;// Must be included last.&#x0A;// clang-format on&#x0A;&#x0A;#endif // third_5fparty_2fprotobuf_2feditions_2fgolden_2fsimple_5fproto3_2eproto_2epb_2eh&#x0A;, with the difference:&#x0A;@@ @@&#x0A; enum : int {&#x0A; kInt32FieldFieldNumber = 1,&#x0A; };&#x0A;- // optional int32 int32_field = 1;&#x0A;+ // int32 int32_field = 1;&#x0A; [[nodiscard]] bool has_int32_field()&#x0A; const;&#x0A; void clear_int32_field() ;&#x0A;@@ @@&#x0A; }&#x0A; // SimpleProto3&#x0A; &#x0A;-// optional int32 int32_field = 1;&#x0A;+// int32 int32_field = 1;&#x0A; inline bool SimpleProto3::has_int32_field() const {&#x0A; bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000001U);&#x0A; return value;" type=""></failure>
99
</testcase>
1010
<testcase name="third_party/protobuf/editions/golden/simple_proto3.proto.static_reflection.h" status="run" result="completed" classname="DiffTest">
1111
</testcase>

src/google/protobuf/compiler/cpp/message.cc

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,15 +2284,7 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* p) {
22842284
CopyFrom(from);
22852285
return *this;
22862286
}
2287-
inline $classname$& operator=($classname$&& from) noexcept {
2288-
if (this == &from) return *this;
2289-
if ($pbi$::CanMoveWithInternalSwap(GetArena(), from.GetArena())) {
2290-
InternalSwap(&from);
2291-
} else {
2292-
CopyFrom(from);
2293-
}
2294-
return *this;
2295-
}
2287+
$classname$& operator=($classname$&& from) noexcept;
22962288
$decl_verify_func$;
22972289
22982290
inline const $unknown_fields_type$& unknown_fields() const
@@ -2352,12 +2344,7 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* p) {
23522344
23532345
explicit $classname$($pb$::Arena* $nullable$ arena);
23542346
$classname$($pb$::Arena* $nullable$ arena, const $classname$& from);
2355-
$classname$(
2356-
//~
2357-
$pb$::Arena* $nullable$ arena, $classname$&& from) noexcept
2358-
: $classname$(arena) {
2359-
*this = ::std::move(from);
2360-
}
2347+
$classname$($pb$::Arena* $nullable$ arena, $classname$&& from) noexcept;
23612348
$arena_dtor$;
23622349
const $pbi$::ClassData* $nonnull$ GetClassData() const PROTOBUF_FINAL;
23632350
static void* $nonnull$ PlacementNew_(
@@ -2416,6 +2403,74 @@ void MessageGenerator::GenerateInlineMethods(io::Printer* p) {
24162403
auto v = p->WithVars(ClassVars(descriptor_, options_));
24172404
auto t = p->WithVars(MakeTrackerCalls(descriptor_, options_));
24182405
if (IsMapEntryMessage(descriptor_)) return;
2406+
2407+
if (CanUseTrivialCopy()) {
2408+
p->Emit(R"cc(
2409+
inline $classname$::$classname$($pb$::Arena* $nullable$ arena,
2410+
const $classname$& from)
2411+
#if defined(PROTOBUF_CUSTOM_VTABLE)
2412+
: $superclass$(arena, $classname$_class_data_.base()),
2413+
#else // PROTOBUF_CUSTOM_VTABLE
2414+
: $superclass$(arena),
2415+
#endif // PROTOBUF_CUSTOM_VTABLE
2416+
_impl_(from._impl_) {
2417+
if (ABSL_PREDICT_FALSE(from._internal_metadata_.have_unknown_fields())) {
2418+
$superclass$::CopyFromUFS<$unknown_fields_type$>(from);
2419+
}
2420+
}
2421+
2422+
inline $classname$::$classname$($pb$::Arena* $nullable$ arena,
2423+
$classname$&& from) noexcept
2424+
#if defined(PROTOBUF_CUSTOM_VTABLE)
2425+
: $superclass$(arena, $classname$_class_data_.base()),
2426+
#else // PROTOBUF_CUSTOM_VTABLE
2427+
: $superclass$(arena),
2428+
#endif // PROTOBUF_CUSTOM_VTABLE
2429+
_impl_(from._impl_) {
2430+
if (ABSL_PREDICT_FALSE(from._internal_metadata_.have_unknown_fields())) {
2431+
$superclass$::MoveFromUFS<$unknown_fields_type$>(arena, from);
2432+
}
2433+
}
2434+
2435+
inline $classname$& $classname$::operator=($classname$&& from) noexcept {
2436+
_impl_ = from._impl_;
2437+
if (ABSL_PREDICT_FALSE(from._internal_metadata_.have_unknown_fields())) {
2438+
$superclass$::MoveAssignFromUFS<$unknown_fields_type$>(from);
2439+
}
2440+
return *this;
2441+
}
2442+
)cc");
2443+
2444+
if (HasGeneratedMethods(descriptor_->file(), options_)) {
2445+
p->Emit(R"cc(
2446+
inline void $classname$::CopyFrom(const $classname$& from) {
2447+
// @@protoc_insertion_point(class_specific_copy_from_start:$full_name$)
2448+
_impl_ = from._impl_;
2449+
if (ABSL_PREDICT_FALSE(from._internal_metadata_.have_unknown_fields())) {
2450+
$superclass$::CopyFromUFS<$unknown_fields_type$>(from);
2451+
}
2452+
}
2453+
)cc");
2454+
}
2455+
} else {
2456+
p->Emit(R"cc(
2457+
inline $classname$::$classname$($pb$::Arena* $nullable$ arena,
2458+
$classname$&& from) noexcept
2459+
: $classname$(arena) {
2460+
*this = ::std::move(from);
2461+
}
2462+
inline $classname$& $classname$::operator=($classname$&& from) noexcept {
2463+
if (this == &from) return *this;
2464+
if ($pbi$::CanMoveWithInternalSwap(GetArena(), from.GetArena())) {
2465+
InternalSwap(&from);
2466+
} else {
2467+
CopyFrom(from);
2468+
}
2469+
return *this;
2470+
}
2471+
)cc");
2472+
}
2473+
24192474
GenerateFieldAccessorDefinitions(p);
24202475

24212476
// Generate oneof_case() functions.
@@ -3571,7 +3626,9 @@ void MessageGenerator::GenerateStructors(io::Printer* p) {
35713626
)cc");
35723627

35733628
// Generate the copy constructor.
3574-
if (UsingImplicitWeakFields(descriptor_->file(), options_)) {
3629+
if (CanUseTrivialCopy()) {
3630+
// Do nothing. Already generated as inline.
3631+
} else if (UsingImplicitWeakFields(descriptor_->file(), options_)) {
35753632
// If we are in lite mode and using implicit weak fields, we generate a
35763633
// one-liner copy constructor that delegates to MergeFrom. This saves some
35773634
// code size and also cuts down on the complexity of implicit weak fields.
@@ -3584,21 +3641,6 @@ void MessageGenerator::GenerateStructors(io::Printer* p) {
35843641
MergeFrom(from);
35853642
}
35863643
)cc");
3587-
} else if (CanUseTrivialCopy()) {
3588-
p->Emit(R"cc(
3589-
$classname$::$classname$(
3590-
//~ Force alignment
3591-
$pb$::Arena* $nullable$ arena, const $classname$& from)
3592-
#if defined(PROTOBUF_CUSTOM_VTABLE)
3593-
: $superclass$(arena, $classname$_class_data_.base()),
3594-
#else // PROTOBUF_CUSTOM_VTABLE
3595-
: $superclass$(arena),
3596-
#endif // PROTOBUF_CUSTOM_VTABLE
3597-
_impl_(from._impl_) {
3598-
_internal_metadata_.MergeFrom<$unknown_fields_type$>(
3599-
from._internal_metadata_);
3600-
}
3601-
)cc");
36023644
} else {
36033645
GenerateArenaEnabledCopyConstructor(p);
36043646
}
@@ -4672,6 +4714,7 @@ void MessageGenerator::GenerateClassSpecificMergeImpl(io::Printer* p) {
46724714

46734715
void MessageGenerator::GenerateCopyFrom(io::Printer* p) {
46744716
if (HasSimpleBaseClass(descriptor_, options_)) return;
4717+
if (CanUseTrivialCopy()) return;
46754718
if (HasDescriptorMethods(descriptor_->file(), options_)) {
46764719
// We don't override the generalized CopyFrom (aka that which
46774720
// takes in the Message base class as a parameter); instead we just

src/google/protobuf/compiler/java/java_features.pb.h

Lines changed: 58 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)