proto, runtime/protoiface, internal/impl: add fast-path Merge
Comparing -tags=protoreflect to fast-path:
name old time/op new time/op delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12 1.70µs ± 1% 0.30µs ± 1% -82.64% (p=0.001 n=7+7)
/Clone/google_message1_proto3-12 1.01µs ± 1% 0.19µs ± 1% -80.77% (p=0.000 n=7+8)
/Clone/google_message2-12 818µs ± 8% 141µs ± 6% -82.78% (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12 51.1ns ± 1% 39.3ns ± 3% -23.03% (p=0.000 n=7+8)
RepeatedInt32/Clone-12 24.5µs ± 1% 1.1µs ± 3% -95.64% (p=0.000 n=8+8)
Required/Clone-12 978ns ± 1% 132ns ± 2% -86.46% (p=0.000 n=8+8)
name old alloc/op new alloc/op delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12 1.08kB ± 0% 0.74kB ± 0% -31.85% (p=0.000 n=8+8)
/Clone/google_message1_proto3-12 872B ± 0% 544B ± 0% -37.61% (p=0.000 n=8+8)
/Clone/google_message2-12 602kB ± 0% 411kB ± 0% -31.65% (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12 96.0B ± 0% 64.0B ± 0% -33.33% (p=0.000 n=8+8)
RepeatedInt32/Clone-12 25.4kB ± 0% 3.2kB ± 0% -87.33% (p=0.000 n=8+8)
Required/Clone-12 416B ± 0% 256B ± 0% -38.46% (p=0.000 n=8+8)
name old allocs/op new allocs/op delta
pkg:google.golang.org/protobuf/internal/benchmarks goos:linux goarch:amd64
/Clone/google_message1_proto2-12 52.0 ± 0% 21.0 ± 0% -59.62% (p=0.000 n=8+8)
/Clone/google_message1_proto3-12 33.0 ± 0% 3.0 ± 0% -90.91% (p=0.000 n=8+8)
/Clone/google_message2-12 22.3k ± 0% 7.5k ± 0% -66.41% (p=0.000 n=8+8)
pkg:google.golang.org/protobuf/internal/benchmarks/micro goos:linux goarch:amd64
EmptyMessage/Clone-12 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=8+8)
RepeatedInt32/Clone-12 1.51k ± 0% 0.00k ± 0% -99.80% (p=0.000 n=8+8)
Required/Clone-12 51.0 ± 0% 18.0 ± 0% -64.71% (p=0.000 n=8+8)
Change-Id: Ife9018097c34cb025dc9c4fdd9a61b2f947853c6
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/219147
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/codec_field.go b/internal/impl/codec_field.go
index dd6a8a1..98992e7 100644
--- a/internal/impl/codec_field.go
+++ b/internal/impl/codec_field.go
@@ -45,7 +45,8 @@
// and dispatches to the field-specific marshaler in oneofFields.
cf := *mi.coderFields[num]
ot := si.oneofWrappersByNumber[num]
- cf.mi, cf.funcs = fieldCoder(fd, ot.Field(0).Type)
+ cf.ft = ot.Field(0).Type
+ cf.mi, cf.funcs = fieldCoder(fd, cf.ft)
oneofFields[ot] = &cf
if cf.funcs.isInit != nil {
needIsInit = true
@@ -92,6 +93,18 @@
}
return info.funcs.marshal(b, p, info, opts)
}
+ first.funcs.merge = func(dst, src pointer, _ *coderFieldInfo, opts mergeOptions) {
+ srcp, srcinfo := getInfo(src)
+ if srcinfo == nil || srcinfo.funcs.merge == nil {
+ return
+ }
+ dstp, dstinfo := getInfo(dst)
+ if dstinfo != srcinfo {
+ dst.AsValueOf(ft).Elem().Set(reflect.New(src.AsValueOf(ft).Elem().Elem().Elem().Type()))
+ dstp = pointerOfValue(dst.AsValueOf(ft).Elem().Elem()).Apply(zeroOffset)
+ }
+ srcinfo.funcs.merge(dstp, srcp, srcinfo, opts)
+ }
if needIsInit {
first.funcs.isInit = func(p pointer, _ *coderFieldInfo) error {
p, info := getInfo(p)
@@ -113,10 +126,9 @@
})
}
- num := fd.Number()
return pointerCoderFuncs{
size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
- m, ok := p.WeakFields().get(num)
+ m, ok := p.WeakFields().get(f.num)
if !ok {
return 0
}
@@ -127,7 +139,7 @@
return sizeMessage(m, f.tagsize, opts)
},
marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
- m, ok := p.WeakFields().get(num)
+ m, ok := p.WeakFields().get(f.num)
if !ok {
return b, nil
}
@@ -139,24 +151,40 @@
},
unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
fs := p.WeakFields()
- m, ok := fs.get(num)
+ m, ok := fs.get(f.num)
if !ok {
lazyInit()
if messageType == nil {
return unmarshalOutput{}, errUnknown
}
m = messageType.New().Interface()
- fs.set(num, m)
+ fs.set(f.num, m)
}
return consumeMessage(b, m, wtyp, opts)
},
isInit: func(p pointer, f *coderFieldInfo) error {
- m, ok := p.WeakFields().get(num)
+ m, ok := p.WeakFields().get(f.num)
if !ok {
return nil
}
return proto.IsInitialized(m)
},
+ merge: func(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
+ sm, ok := src.WeakFields().get(f.num)
+ if !ok {
+ return
+ }
+ dm, ok := dst.WeakFields().get(f.num)
+ if !ok {
+ lazyInit()
+ if messageType == nil {
+ panic(fmt.Sprintf("weak message %v is not linked in", fd.Message().FullName()))
+ }
+ dm = messageType.New().Interface()
+ dst.WeakFields().set(f.num, dm)
+ }
+ opts.Merge(dm, sm)
+ },
}
}
@@ -166,6 +194,7 @@
size: sizeMessageInfo,
marshal: appendMessageInfo,
unmarshal: consumeMessageInfo,
+ merge: mergeMessage,
}
if needsInitCheck(mi.Desc) {
funcs.isInit = isInitMessageInfo
@@ -192,6 +221,7 @@
m := asMessage(p.AsValueOf(ft).Elem())
return proto.IsInitialized(m)
},
+ merge: mergeMessage,
}
}
}
@@ -285,6 +315,7 @@
marshal: appendMessageValue,
unmarshal: consumeMessageValue,
isInit: isInitMessageValue,
+ merge: mergeMessageValue,
}
func sizeGroupValue(v pref.Value, tagsize int, opts marshalOptions) int {
@@ -308,6 +339,7 @@
marshal: appendGroupValue,
unmarshal: consumeGroupValue,
isInit: isInitMessageValue,
+ merge: mergeMessageValue,
}
func makeGroupFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
@@ -317,6 +349,7 @@
size: sizeGroupType,
marshal: appendGroupType,
unmarshal: consumeGroupType,
+ merge: mergeMessage,
}
if needsInitCheck(mi.Desc) {
funcs.isInit = isInitMessageInfo
@@ -343,6 +376,7 @@
m := asMessage(p.AsValueOf(ft).Elem())
return proto.IsInitialized(m)
},
+ merge: mergeMessage,
}
}
}
@@ -404,6 +438,7 @@
size: sizeMessageSliceInfo,
marshal: appendMessageSliceInfo,
unmarshal: consumeMessageSliceInfo,
+ merge: mergeMessageSlice,
}
if needsInitCheck(mi.Desc) {
funcs.isInit = isInitMessageSliceInfo
@@ -423,6 +458,7 @@
isInit: func(p pointer, f *coderFieldInfo) error {
return isInitMessageSlice(p, ft)
},
+ merge: mergeMessageSlice,
}
}
@@ -605,6 +641,7 @@
marshal: appendMessageSliceValue,
unmarshal: consumeMessageSliceValue,
isInit: isInitMessageSliceValue,
+ merge: mergeMessageListValue,
}
func sizeGroupSliceValue(listv pref.Value, tagsize int, opts marshalOptions) int {
@@ -660,6 +697,7 @@
marshal: appendGroupSliceValue,
unmarshal: consumeGroupSliceValue,
isInit: isInitMessageSliceValue,
+ merge: mergeMessageListValue,
}
func makeGroupSliceFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
@@ -669,6 +707,7 @@
size: sizeGroupSliceInfo,
marshal: appendGroupSliceInfo,
unmarshal: consumeGroupSliceInfo,
+ merge: mergeMessageSlice,
}
if needsInitCheck(mi.Desc) {
funcs.isInit = isInitMessageSliceInfo
@@ -688,6 +727,7 @@
isInit: func(p pointer, f *coderFieldInfo) error {
return isInitMessageSlice(p, ft)
},
+ merge: mergeMessageSlice,
}
}