Skip to content

Commit 88316cf

Browse files
authored
Merge pull request #73 from goccy/feature/improve-performance
Improve encoding performance
2 parents 26e4f7f + f5daa59 commit 88316cf

File tree

4 files changed

+68
-96
lines changed

4 files changed

+68
-96
lines changed

encode.go

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ import (
1515

1616
// An Encoder writes JSON values to an output stream.
1717
type Encoder struct {
18-
w io.Writer
19-
ctx *encodeRuntimeContext
20-
buf []byte
21-
enabledIndent bool
22-
enabledHTMLEscape bool
23-
unorderedMap bool
24-
prefix []byte
25-
indentStr []byte
26-
structTypeToCompiledCode map[uintptr]*compiledCode
27-
structTypeToCompiledIndentCode map[uintptr]*compiledCode
18+
w io.Writer
19+
ctx *encodeRuntimeContext
20+
ptr unsafe.Pointer
21+
buf []byte
22+
enabledIndent bool
23+
enabledHTMLEscape bool
24+
unorderedMap bool
25+
prefix []byte
26+
indentStr []byte
2827
}
2928

3029
type compiledCode struct {
@@ -73,9 +72,7 @@ func init() {
7372
ptrs: make([]uintptr, 128),
7473
keepRefs: make([]unsafe.Pointer, 0, 8),
7574
},
76-
buf: make([]byte, 0, bufSize),
77-
structTypeToCompiledCode: map[uintptr]*compiledCode{},
78-
structTypeToCompiledIndentCode: map[uintptr]*compiledCode{},
75+
buf: make([]byte, 0, bufSize),
7976
}
8077
},
8178
}
@@ -105,20 +102,20 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
105102
return err
106103
}
107104
}
108-
var err error
109-
if e.buf, err = e.encode(v); err != nil {
105+
buf, err := e.encode(v)
106+
if err != nil {
110107
return err
111108
}
112109
if e.enabledIndent {
113-
e.buf = e.buf[:len(e.buf)-2]
110+
buf = buf[:len(buf)-2]
114111
} else {
115-
e.buf = e.buf[:len(e.buf)-1]
112+
buf = buf[:len(buf)-1]
116113
}
117-
e.buf = append(e.buf, '\n')
118-
if _, err := e.w.Write(e.buf); err != nil {
114+
buf = append(buf, '\n')
115+
if _, err := e.w.Write(buf); err != nil {
119116
return err
120117
}
121-
e.buf = e.buf[:0]
118+
e.buf = buf[:0]
122119
return nil
123120
}
124121

@@ -148,29 +145,31 @@ func (e *Encoder) release() {
148145
}
149146

150147
func (e *Encoder) reset() {
151-
e.buf = e.buf[:0]
152148
e.enabledHTMLEscape = true
153149
e.enabledIndent = false
154150
e.unorderedMap = false
155151
}
156152

157153
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
158-
var err error
159-
if e.buf, err = e.encode(v); err != nil {
154+
buf, err := e.encode(v)
155+
if err != nil {
160156
return nil, err
161157
}
158+
159+
e.buf = buf
160+
162161
if e.enabledIndent {
163-
copied := make([]byte, len(e.buf)-2)
164-
copy(copied, e.buf)
162+
copied := make([]byte, len(buf)-2)
163+
copy(copied, buf)
165164
return copied, nil
166165
}
167-
copied := make([]byte, len(e.buf)-1)
168-
copy(copied, e.buf)
166+
copied := make([]byte, len(buf)-1)
167+
copy(copied, buf)
169168
return copied, nil
170169
}
171170

172171
func (e *Encoder) encode(v interface{}) ([]byte, error) {
173-
b := e.buf
172+
b := e.buf[:0]
174173
if v == nil {
175174
b = encodeNull(b)
176175
if e.enabledIndent {
@@ -202,17 +201,19 @@ func (e *Encoder) encode(v interface{}) ([]byte, error) {
202201
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
203202

204203
codeIndent, err := e.compileHead(&encodeCompileContext{
205-
typ: copiedType,
206-
root: true,
207-
withIndent: true,
204+
typ: copiedType,
205+
root: true,
206+
withIndent: true,
207+
structTypeToCompiledCode: map[uintptr]*compiledCode{},
208208
})
209209
if err != nil {
210210
return nil, err
211211
}
212212
code, err := e.compileHead(&encodeCompileContext{
213-
typ: copiedType,
214-
root: true,
215-
withIndent: false,
213+
typ: copiedType,
214+
root: true,
215+
withIndent: false,
216+
structTypeToCompiledCode: map[uintptr]*compiledCode{},
216217
})
217218
if err != nil {
218219
return nil, err

encode_compile.go

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package json
22

33
import (
4-
"bytes"
54
"fmt"
65
"reflect"
76
"unsafe"
@@ -877,14 +876,8 @@ func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *o
877876
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
878877
typ := ctx.typ
879878
typeptr := uintptr(unsafe.Pointer(typ))
880-
if ctx.withIndent {
881-
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
882-
return e.recursiveCode(ctx, compiledCode)
883-
}
884-
} else {
885-
if compiledCode, exists := e.structTypeToCompiledCode[typeptr]; exists {
886-
return e.recursiveCode(ctx, compiledCode)
887-
}
879+
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
880+
return e.recursiveCode(ctx, compiledCode)
888881
}
889882
return nil
890883
}
@@ -1140,11 +1133,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
11401133
typ := ctx.typ
11411134
typeptr := uintptr(unsafe.Pointer(typ))
11421135
compiled := &compiledCode{}
1143-
if ctx.withIndent {
1144-
e.structTypeToCompiledIndentCode[typeptr] = compiled
1145-
} else {
1146-
e.structTypeToCompiledCode[typeptr] = compiled
1147-
}
1136+
ctx.structTypeToCompiledCode[typeptr] = compiled
11481137
// header => code => structField => code => end
11491138
// ^ |
11501139
// |__________|
@@ -1212,12 +1201,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
12121201
}
12131202
}
12141203
key := fmt.Sprintf(`"%s":`, tag.key)
1215-
1216-
var buf bytes.Buffer
1217-
enc := NewEncoder(&buf)
1218-
enc.buf = encodeEscapedString(enc.buf, tag.key)
1219-
escapedKey := fmt.Sprintf(`%s:`, string(enc.buf))
1220-
enc.release()
1204+
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
12211205
fieldCode := &opcode{
12221206
typ: valueCode.typ,
12231207
displayIdx: fieldOpcodeIndex,
@@ -1294,11 +1278,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
12941278
ret := (*opcode)(unsafe.Pointer(head))
12951279
compiled.code = ret
12961280

1297-
if ctx.withIndent {
1298-
delete(e.structTypeToCompiledIndentCode, typeptr)
1299-
} else {
1300-
delete(e.structTypeToCompiledCode, typeptr)
1301-
}
1281+
delete(ctx.structTypeToCompiledCode, typeptr)
13021282

13031283
return ret, nil
13041284
}

encode_context.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,27 @@ import (
55
)
66

77
type encodeCompileContext struct {
8-
typ *rtype
9-
withIndent bool
10-
root bool
11-
opcodeIndex int
12-
ptrIndex int
13-
indent int
8+
typ *rtype
9+
withIndent bool
10+
root bool
11+
opcodeIndex int
12+
ptrIndex int
13+
indent int
14+
structTypeToCompiledCode map[uintptr]*compiledCode
1415

1516
parent *encodeCompileContext
1617
}
1718

1819
func (c *encodeCompileContext) context() *encodeCompileContext {
1920
return &encodeCompileContext{
20-
typ: c.typ,
21-
withIndent: c.withIndent,
22-
root: c.root,
23-
opcodeIndex: c.opcodeIndex,
24-
ptrIndex: c.ptrIndex,
25-
indent: c.indent,
26-
parent: c,
21+
typ: c.typ,
22+
withIndent: c.withIndent,
23+
root: c.root,
24+
opcodeIndex: c.opcodeIndex,
25+
ptrIndex: c.ptrIndex,
26+
indent: c.indent,
27+
structTypeToCompiledCode: c.structTypeToCompiledCode,
28+
parent: c,
2729
}
2830
}
2931

encode_vm.go

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,11 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
317317
}
318318
}
319319
c, err := e.compileHead(&encodeCompileContext{
320-
typ: header.typ,
321-
root: code.root,
322-
withIndent: e.enabledIndent,
323-
indent: code.indent,
320+
typ: header.typ,
321+
root: code.root,
322+
withIndent: e.enabledIndent,
323+
indent: code.indent,
324+
structTypeToCompiledCode: map[uintptr]*compiledCode{},
324325
})
325326
if err != nil {
326327
return nil, err
@@ -4157,17 +4158,13 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
41574158
} else {
41584159
b = append(b, '{')
41594160
b = e.encodeKey(b, code)
4160-
var buf bytes.Buffer
4161-
enc := NewEncoder(&buf)
41624161
s := e.ptrToString(ptr + code.offset)
41634162
if e.enabledHTMLEscape {
4164-
enc.buf = encodeEscapedString(enc.buf, s)
4163+
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
41654164
} else {
4166-
enc.buf = encodeNoEscapedString(enc.buf, s)
4165+
b = e.encodeString(b, string(encodeNoEscapedString([]byte{}, s)))
41674166
}
4168-
b = e.encodeString(b, string(enc.buf))
41694167
b = encodeComma(b)
4170-
enc.release()
41714168
code = code.next
41724169
}
41734170
case opStructFieldPtrAnonymousHeadStringTagString:
@@ -4706,15 +4703,12 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
47064703
b = e.encodeIndent(b, code.indent+1)
47074704
b = e.encodeKey(b, code)
47084705
b = append(b, ' ')
4709-
var buf bytes.Buffer
4710-
enc := NewEncoder(&buf)
47114706
s := e.ptrToString(ptr + code.offset)
47124707
if e.enabledHTMLEscape {
4713-
enc.buf = encodeEscapedString(enc.buf, s)
4708+
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
47144709
} else {
4715-
enc.buf = encodeNoEscapedString(enc.buf, s)
4710+
b = e.encodeString(b, string(encodeNoEscapedString([]byte{}, s)))
47164711
}
4717-
b = e.encodeString(b, string(enc.buf))
47184712
b = encodeIndentComma(b)
47194713
code = code.next
47204714
}
@@ -5908,10 +5902,8 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
59085902
case opStructFieldStringTagString:
59095903
ptr := load(ctxptr, code.headIdx)
59105904
b = e.encodeKey(b, code)
5911-
var buf bytes.Buffer
5912-
enc := NewEncoder(&buf)
5913-
enc.buf = enc.encodeString(enc.buf, e.ptrToString(ptr+code.offset))
5914-
b = e.encodeString(b, string(enc.buf))
5905+
s := e.ptrToString(ptr + code.offset)
5906+
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
59155907
code = code.next
59165908
case opStructFieldStringTagBool:
59175909
ptr := load(ctxptr, code.headIdx)
@@ -6065,12 +6057,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
60656057
b = e.encodeIndent(b, code.indent)
60666058
b = e.encodeKey(b, code)
60676059
b = append(b, ' ')
6068-
var buf bytes.Buffer
6069-
enc := NewEncoder(&buf)
6070-
enc.buf = enc.encodeString(enc.buf, e.ptrToString(ptr+code.offset))
6071-
b = e.encodeString(b, string(enc.buf))
6060+
s := e.ptrToString(ptr + code.offset)
6061+
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
60726062
b = encodeIndentComma(b)
6073-
enc.release()
60746063
code = code.next
60756064
case opStructFieldStringTagBoolIndent:
60766065
ptr := load(ctxptr, code.headIdx)

0 commit comments

Comments
 (0)