internal/impl: add MessageState to every generated message
We define MessageState, which is essentially an atomically set *MessageInfo.
By nesting this as the first field in every generated message, we can
implement the reflective methods on a *MessageState when obtained by
unsafe casting a concrete message pointer as a *MessageState.
The MessageInfo held by MessageState provides additional Go type information
to interpret the memory that comes after the contents of the MessageState.
Since we are nesting a MessageState in every message,
the memory use of every message instance grows by 8B.
On average, the body of ProtoReflect grows from 133B to 202B (+50%).
However, this is offset by XXX_Methods, which is 108B and
will be removed in a future CL. Taking into account the eventual removal
of XXX_Methods, this is a net reduction of 25%.
name old time/op new time/op delta
Name/Value-4 70.3ns ± 2% 17.5ns ± 6% -75.08% (p=0.000 n=10+10)
Name/Nil-4 70.6ns ± 3% 33.4ns ± 2% -52.66% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
Name/Value-4 16.0B ± 0% 0.0B -100.00% (p=0.000 n=10+10)
Name/Nil-4 16.0B ± 0% 0.0B -100.00% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
Name/Value-4 1.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10)
Name/Nil-4 1.00 ± 0% 0.00 -100.00% (p=0.000 n=10+10)
Change-Id: I92bd58dc681c57c92612fd5ba7fc066aea34e95a
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185460
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index 219d79a..67f3071 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -48,10 +48,14 @@
// ExtensionRangeArray method for messages that support extensions.
generateExtensionRangeMethods = true
- // generateWKTMarkerMethods specifes whether to generate
+ // generateWKTMarkerMethods specifies whether to generate
// XXX_WellKnownType methods on well-known types.
generateWKTMarkerMethods = false
+ // generateMessateStateFields specifies whether to generate an unexported
+ // protoimpl.MessageState as the first field.
+ generateMessateStateFields = true
+
// generateNoUnkeyedLiteralFields specifies whether to generate
// the XXX_NoUnkeyedLiteral field.
generateNoUnkeyedLiteralFields = false
@@ -395,6 +399,10 @@
g.Annotate(message.GoIdent.GoName, message.Location)
g.P("type ", message.GoIdent, " struct {")
sf := f.allMessageFieldsByPtr[message]
+ if generateMessateStateFields {
+ g.P("state ", protoimplPackage.Ident("MessageState"))
+ sf.append("state")
+ }
for _, field := range message.Fields {
if field.Oneof != nil {
// It would be a bit simpler to iterate over the oneofs below,