From de9682ad1656cd1ee6808865f7a42f5a5d79eb73 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 20 Jul 2022 10:12:14 -0700 Subject: [PATCH] internal/impl: improve MessageInfo.New performance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Lasse Folger --- internal/benchmarks/micro/micro_test.go | 8 ++++++++ internal/impl/message.go | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/benchmarks/micro/micro_test.go b/internal/benchmarks/micro/micro_test.go index dd967448b..cbfb2449c 100644 --- a/internal/benchmarks/micro/micro_test.go +++ b/internal/benchmarks/micro/micro_test.go @@ -67,6 +67,14 @@ func BenchmarkEmptyMessage(b *testing.B) { } }) }) + 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 3945ab2ed..4f5fb67a0 100644 --- a/internal/impl/message.go +++ b/internal/impl/message.go @@ -218,7 +218,11 @@ fieldLoop: } 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())