Преглед изворни кода

encoding: optimize for oneofs

Suppose a oneof has N fields, the previous marshaling logic
would traverse every field checking for presence. This is O(N).
Using the protoreflect.Message.WhichOneof method, we can reduce
this to O(1). This optimization is exceptionally useful for oneofs
with a large number of fields.

Change-Id: I5f4aa8b1a899930f5c95e9cf1d68bac4b0b7884d
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/196121
Reviewed-by: Herbie Ong <herbie@google.com>
Joe Tsai пре 6 година
родитељ
комит
fc5f8c340a
2 измењених фајлова са 20 додато и 4 уклоњено
  1. 12 2
      encoding/protojson/encode.go
  2. 8 2
      encoding/prototext/encode.go

+ 12 - 2
encoding/protojson/encode.go

@@ -119,11 +119,21 @@ func (o MarshalOptions) marshalFields(m pref.Message) error {
 
 
 	// Marshal out known fields.
 	// Marshal out known fields.
 	fieldDescs := messageDesc.Fields()
 	fieldDescs := messageDesc.Fields()
-	for i := 0; i < fieldDescs.Len(); i++ {
+	for i := 0; i < fieldDescs.Len(); {
 		fd := fieldDescs.Get(i)
 		fd := fieldDescs.Get(i)
+		if od := fd.ContainingOneof(); od != nil {
+			fd = m.WhichOneof(od)
+			i += od.Fields().Len()
+			if fd == nil {
+				continue // unpopulated oneofs are not affected by EmitUnpopulated
+			}
+		} else {
+			i++
+		}
+
 		val := m.Get(fd)
 		val := m.Get(fd)
 		if !m.Has(fd) {
 		if !m.Has(fd) {
-			if !o.EmitUnpopulated || fd.ContainingOneof() != nil {
+			if !o.EmitUnpopulated {
 				continue
 				continue
 			}
 			}
 			isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid()
 			isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid()

+ 8 - 2
encoding/prototext/encode.go

@@ -101,9 +101,15 @@ func (o MarshalOptions) marshalMessage(m pref.Message) (text.Value, error) {
 	var msgFields [][2]text.Value
 	var msgFields [][2]text.Value
 	fieldDescs := messageDesc.Fields()
 	fieldDescs := messageDesc.Fields()
 	size := fieldDescs.Len()
 	size := fieldDescs.Len()
-	for i := 0; i < size; i++ {
+	for i := 0; i < size; {
 		fd := fieldDescs.Get(i)
 		fd := fieldDescs.Get(i)
-		if !m.Has(fd) {
+		if od := fd.ContainingOneof(); od != nil {
+			fd = m.WhichOneof(od)
+			i += od.Fields().Len()
+		} else {
+			i++
+		}
+		if fd == nil || !m.Has(fd) {
 			continue
 			continue
 		}
 		}