proto, internal/impl: avoid string->[]byte conversions

We trust the compiler to optimize away the string->[]byte conversion in
code like:

	b = wire.AppendBytes(b, []byte(s))

In testing (go 1.12.5 linux/amd64), this optimization is not happening.
Perhaps newer versions of the compiler will optimize this, but we
shouldn't rely on it; avoid unnecessary conversions.

Benchmark differences vs https://golang.org/cl/171462:

  name                                   old time/op    new time/op    delta
  Wire/Marshal/google_message1_proto2-6     310ns ± 2%     189ns ± 3%  -39.20%  (p=0.000 n=8+8)
  Wire/Marshal/google_message1_proto3-6     389ns ± 8%     261ns ± 2%  -33.03%  (p=0.000 n=8+8)
  Wire/Marshal/google_message2-6            103µs ±11%      59µs ± 4%  -42.17%  (p=0.000 n=8+8)

  name                                   old alloc/op   new alloc/op   delta
  Wire/Marshal/google_message1_proto2-6      592B ± 0%      240B ± 0%  -59.46%  (p=0.000 n=8+8)
  Wire/Marshal/google_message1_proto3-6      576B ± 0%      224B ± 0%  -61.11%  (p=0.000 n=8+8)
  Wire/Marshal/google_message2-6            196kB ± 0%      90kB ± 0%  -54.05%  (p=0.000 n=8+8)

  name                                   old allocs/op  new allocs/op  delta
  Wire/Marshal/google_message1_proto2-6      5.00 ± 0%      1.00 ± 0%  -80.00%  (p=0.000 n=8+8)
  Wire/Marshal/google_message1_proto3-6      5.00 ± 0%      1.00 ± 0%  -80.00%  (p=0.000 n=8+8)
  Wire/Marshal/google_message2-6            1.66k ± 0%     0.00k ± 0%  -99.94%  (p=0.000 n=8+8)

Change-Id: Idab7634b8c86604dffa46895ba2e61be38c9bd9c
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/183380
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
This commit is contained in:
Damien Neil 2019-06-21 12:04:07 -07:00
parent 8c86fc5e7d
commit cedb595154
7 changed files with 33 additions and 24 deletions

View File

@ -43,8 +43,12 @@ wire.Size{{.WireType}}({{.FromGoType}})
Append is a set of statements appending 'v' to 'b'.
*/ -}}
{{- define "Append" -}}
{{- if eq .Name "String" -}}
b = wire.AppendString(b, {{.FromGoType}})
{{- else -}}
b = wire.Append{{.WireType}}(b, {{.FromGoType}})
{{- end -}}
{{- end -}}
{{- range .}}
{{- if .FromGoType }}

View File

@ -209,9 +209,9 @@ var ProtoKinds = []ProtoKind{
Name: "String",
WireType: WireBytes,
ToValue: "string(v)",
FromValue: "[]byte(v.String())",
FromValue: "v.String()",
GoType: GoString,
FromGoType: "[]byte(v)",
FromGoType: "v",
},
{
Name: "Bytes",
@ -344,8 +344,8 @@ func (o MarshalOptions) marshalSingular(b []byte, fd protoreflect.FieldDescripto
if fd.Syntax() == protoreflect.Proto3 && !utf8.ValidString(v.String()) {
return b, errors.InvalidUTF8(string(fd.FullName()))
}
{{end -}}
{{- if (eq .Name "Message") -}}
b = wire.AppendString(b, {{.FromValue}})
{{- else if (eq .Name "Message") -}}
var pos int
var err error
b, pos = appendSpeculativeLength(b)

View File

@ -425,6 +425,11 @@ func AppendBytes(b []byte, v []byte) []byte {
return append(AppendVarint(b, uint64(len(v))), v...)
}
// AppendString appends v to b as a lenght-prefixed bytes value.
func AppendString(b []byte, v string) []byte {
return append(AppendVarint(b, uint64(len(v))), v...)
}
// ConsumeBytes parses b as a length-prefixed bytes value, reporting its length.
// This returns a negative length upon an error (see ParseError).
func ConsumeBytes(b []byte) (v []byte, n int) {

View File

@ -412,7 +412,7 @@ var coderEnumSliceIface = ifaceCoderFuncs{
func appendStringValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
v := *p.String()
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
if !utf8.ValidString(v) {
return b, errInvalidUTF8{}
}
@ -430,7 +430,7 @@ func appendStringNoZeroValidateUTF8(b []byte, p pointer, wiretag uint64, _ marsh
return b, nil
}
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
if !utf8.ValidString(v) {
return b, errInvalidUTF8{}
}
@ -445,7 +445,7 @@ var coderStringNoZeroValidateUTF8 = pointerCoderFuncs{
func sizeStringSliceValidateUTF8(p pointer, tagsize int, _ marshalOptions) (size int) {
s := *p.StringSlice()
for _, v := range s {
size += tagsize + wire.SizeBytes(len([]byte(v)))
size += tagsize + wire.SizeBytes(len(v))
}
return size
}
@ -455,7 +455,7 @@ func appendStringSliceValidateUTF8(b []byte, p pointer, wiretag uint64, _ marsha
var err error
for _, v := range s {
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
if !utf8.ValidString(v) {
err = errInvalidUTF8{}
}
@ -470,13 +470,13 @@ var coderStringSliceValidateUTF8 = pointerCoderFuncs{
func sizeStringIfaceValidateUTF8(ival interface{}, tagsize int, _ marshalOptions) int {
v := ival.(string)
return tagsize + wire.SizeBytes(len([]byte(v)))
return tagsize + wire.SizeBytes(len(v))
}
func appendStringIfaceValidateUTF8(b []byte, ival interface{}, wiretag uint64, _ marshalOptions) ([]byte, error) {
v := ival.(string)
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
if !utf8.ValidString(v) {
return b, errInvalidUTF8{}
}

View File

@ -2154,14 +2154,14 @@ var coderDoubleSliceIface = ifaceCoderFuncs{
// sizeString returns the size of wire encoding a string pointer as a String.
func sizeString(p pointer, tagsize int, _ marshalOptions) (size int) {
v := *p.String()
return tagsize + wire.SizeBytes(len([]byte(v)))
return tagsize + wire.SizeBytes(len(v))
}
// appendString wire encodes a string pointer as a String.
func appendString(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
v := *p.String()
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
return b, nil
}
@ -2177,7 +2177,7 @@ func sizeStringNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
if len(v) == 0 {
return 0
}
return tagsize + wire.SizeBytes(len([]byte(v)))
return tagsize + wire.SizeBytes(len(v))
}
// appendString wire encodes a string pointer as a String.
@ -2188,7 +2188,7 @@ func appendStringNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) (
return b, nil
}
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
return b, nil
}
@ -2201,7 +2201,7 @@ var coderStringNoZero = pointerCoderFuncs{
// It panics if the pointer is nil.
func sizeStringPtr(p pointer, tagsize int, _ marshalOptions) (size int) {
v := **p.StringPtr()
return tagsize + wire.SizeBytes(len([]byte(v)))
return tagsize + wire.SizeBytes(len(v))
}
// appendString wire encodes a *string pointer as a String.
@ -2209,7 +2209,7 @@ func sizeStringPtr(p pointer, tagsize int, _ marshalOptions) (size int) {
func appendStringPtr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
v := **p.StringPtr()
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
return b, nil
}
@ -2222,7 +2222,7 @@ var coderStringPtr = pointerCoderFuncs{
func sizeStringSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
s := *p.StringSlice()
for _, v := range s {
size += tagsize + wire.SizeBytes(len([]byte(v)))
size += tagsize + wire.SizeBytes(len(v))
}
return size
}
@ -2232,7 +2232,7 @@ func appendStringSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([
s := *p.StringSlice()
for _, v := range s {
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
}
return b, nil
}
@ -2245,14 +2245,14 @@ var coderStringSlice = pointerCoderFuncs{
// sizeStringIface returns the size of wire encoding a string value as a String.
func sizeStringIface(ival interface{}, tagsize int, _ marshalOptions) int {
v := ival.(string)
return tagsize + wire.SizeBytes(len([]byte(v)))
return tagsize + wire.SizeBytes(len(v))
}
// appendStringIface encodes a string value as a String.
func appendStringIface(b []byte, ival interface{}, wiretag uint64, _ marshalOptions) ([]byte, error) {
v := ival.(string)
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
return b, nil
}
@ -2265,7 +2265,7 @@ var coderStringIface = ifaceCoderFuncs{
func sizeStringSliceIface(ival interface{}, tagsize int, _ marshalOptions) (size int) {
s := *ival.(*[]string)
for _, v := range s {
size += tagsize + wire.SizeBytes(len([]byte(v)))
size += tagsize + wire.SizeBytes(len(v))
}
return size
}
@ -2275,7 +2275,7 @@ func appendStringSliceIface(b []byte, ival interface{}, wiretag uint64, _ marsha
s := *ival.(*[]string)
for _, v := range s {
b = wire.AppendVarint(b, wiretag)
b = wire.AppendBytes(b, []byte(v))
b = wire.AppendString(b, v)
}
return b, nil
}

View File

@ -70,7 +70,7 @@ func (o MarshalOptions) marshalSingular(b []byte, fd protoreflect.FieldDescripto
if fd.Syntax() == protoreflect.Proto3 && !utf8.ValidString(v.String()) {
return b, errors.InvalidUTF8(string(fd.FullName()))
}
b = wire.AppendBytes(b, []byte(v.String()))
b = wire.AppendString(b, v.String())
case protoreflect.BytesKind:
b = wire.AppendBytes(b, v.Bytes())
case protoreflect.MessageKind:

View File

@ -42,7 +42,7 @@ func sizeSingular(num wire.Number, kind protoreflect.Kind, v protoreflect.Value)
case protoreflect.DoubleKind:
return wire.SizeFixed64()
case protoreflect.StringKind:
return wire.SizeBytes(len([]byte(v.String())))
return wire.SizeBytes(len(v.String()))
case protoreflect.BytesKind:
return wire.SizeBytes(len(v.Bytes()))
case protoreflect.MessageKind: