]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: remove a branch in the structEncoder loop
authorDaniel Martí <mvdan@mvdan.cc>
Sat, 25 Aug 2018 15:29:01 +0000 (16:29 +0100)
committerDaniel Martí <mvdan@mvdan.cc>
Sat, 25 Aug 2018 23:59:43 +0000 (23:59 +0000)
Encoders like map and array can use the much cheaper "i > 0" check to
see if we're not writing the first element. However, since struct fields
support omitempty, we need to keep track of that separately.

This is much more expensive - after calling the field encoder itself,
and retrieving the field via reflection, this branch was the third most
expensive piece of this field loop.

Instead, hoist the branch logic outside of the loop. The code doesn't
get much more complex, since we just delay the writing of each byte
until the next iteration. Yet the performance improvement is noticeable,
even when the struct types in CodeEncoder only have 2 and 7 fields,
respectively.

name           old time/op    new time/op    delta
CodeEncoder-4    5.39ms ± 0%    5.31ms ± 0%  -1.37%  (p=0.010 n=4+6)

name           old speed      new speed      delta
CodeEncoder-4   360MB/s ± 0%   365MB/s ± 0%  +1.39%  (p=0.010 n=4+6)

Updates #5683.

Change-Id: I2662cf459e0dfd68e56fa52bc898a417e84266c2
Reviewed-on: https://go-review.googlesource.com/131401
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/encoding/json/encode.go

index ec49ceb93ee00deb64883cd0c815db9d2c1db51d..7e5e209b4f2b6a15de51488cea3598ab906a2590 100644 (file)
@@ -628,8 +628,7 @@ type structEncoder struct {
 }
 
 func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
-       e.WriteByte('{')
-       first := true
+       next := byte('{')
        for i := range se.fields {
                f := &se.fields[i]
 
@@ -649,11 +648,8 @@ func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
                if f.omitEmpty && isEmptyValue(fv) {
                        continue
                }
-               if first {
-                       first = false
-               } else {
-                       e.WriteByte(',')
-               }
+               e.WriteByte(next)
+               next = ','
                if opts.escapeHTML {
                        e.WriteString(f.nameEscHTML)
                } else {
@@ -662,7 +658,11 @@ func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
                opts.quoted = f.quoted
                f.encoder(e, fv, opts)
        }
-       e.WriteByte('}')
+       if next == '{' {
+               e.WriteString("{}")
+       } else {
+               e.WriteByte('}')
+       }
 }
 
 func newStructEncoder(t reflect.Type) encoderFunc {