internal/impl: improve MessageInfo.New performance
Calling the ProtoReflect method of the newly-constructed
message avoids an allocation in MessageInfo.MessageOf in
the common case of a generated message with an optimized
ProtoReflect method.
Benchmark for creating an empty message, darwin/arm64 M1 laptop:
name old time/op new time/op delta
EmptyMessage/New-10 32.1ns ± 2% 23.7ns ± 2% -26.06% (p=0.000 n=10+9)
name old alloc/op new alloc/op delta
EmptyMessage/New-10 64.0B ± 0% 48.0B ± 0% -25.00% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
EmptyMessage/New-10 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=10+10)
Change-Id: Ifa3c3ffa8edc76f78399306d0f4964eae4aacd28
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/418677
Reviewed-by: Michael Stapelberg <stapelberg@google.com>
Reviewed-by: Lasse Folger <lassefolger@google.com>
diff --git a/internal/benchmarks/micro/micro_test.go b/internal/benchmarks/micro/micro_test.go
index dd96744..cbfb244 100644
--- a/internal/benchmarks/micro/micro_test.go
+++ b/internal/benchmarks/micro/micro_test.go
@@ -67,6 +67,14 @@
}
})
})
+ b.Run("New", func(b *testing.B) {
+ mt := (&emptypb.Empty{}).ProtoReflect().Type()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ mt.New()
+ }
+ })
+ })
}
// BenchmarkRepeatedInt32 tests a message containing 500 non-packed repeated int32s.
diff --git a/internal/impl/message.go b/internal/impl/message.go
index 3945ab2..4f5fb67 100644
--- a/internal/impl/message.go
+++ b/internal/impl/message.go
@@ -218,7 +218,11 @@
}
func (mi *MessageInfo) New() protoreflect.Message {
- return mi.MessageOf(reflect.New(mi.GoReflectType.Elem()).Interface())
+ m := reflect.New(mi.GoReflectType.Elem()).Interface()
+ if r, ok := m.(protoreflect.ProtoMessage); ok {
+ return r.ProtoReflect()
+ }
+ return mi.MessageOf(m)
}
func (mi *MessageInfo) Zero() protoreflect.Message {
return mi.MessageOf(reflect.Zero(mi.GoReflectType).Interface())