From c21ba224ec88c2a5cb01dad54f06819ed29d4ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 25 Aug 2018 16:29:01 +0100 Subject: [PATCH] encoding/json: remove a branch in the structEncoder loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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í Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/encoding/json/encode.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index ec49ceb93ee..7e5e209b4f2 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -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 {