Skip to content

Commit

Permalink
Merge pull request #259 from goccy/feature/improve-encoding-interface…
Browse files Browse the repository at this point in the history
…-perf

Improve encoding performance of empty interface type
  • Loading branch information
goccy committed Jun 25, 2021
2 parents 923c0f7 + 12e4bdc commit 3829400
Show file tree
Hide file tree
Showing 16 changed files with 691 additions and 574 deletions.
1 change: 1 addition & 0 deletions internal/cmd/generator/main.go
Expand Up @@ -200,6 +200,7 @@ func (t OpType) FieldToOmitEmptyField() OpType {
createOpType("Recursive", "Op"),
createOpType("RecursivePtr", "Op"),
createOpType("RecursiveEnd", "Op"),
createOpType("InterfaceEnd", "Op"),
createOpType("StructAnonymousEnd", "StructEnd"),
}
for _, typ := range primitiveTypesUpper {
Expand Down
47 changes: 42 additions & 5 deletions internal/cmd/generator/vm.go.tmpl
Expand Up @@ -190,15 +190,52 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)

ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
ctxptr = ctx.Ptr()

totalLength := uintptr(code.Length) + 3
nextTotalLength := uintptr(ifaceCodeSet.CodeLength) + 3

var c *encoder.Opcode
if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 {
c = ifaceCodeSet.InterfaceEscapeKeyCode
} else {
c = ifaceCodeSet.InterfaceNoescapeKeyCode
}
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop

newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr

end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))
code = c
case encoder.OpInterfaceEnd:
offset := load(ctxptr, code.Idx)
// restore ctxptr
restoreIndent(ctx, code, ctxptr)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]

b = bb
code = code.Next
codePtr := load(ctxptr, code.ElemIdx)
code = (*encoder.Opcode)(ptrToUnsafePtr(codePtr))
ctxptr = ctx.Ptr() + offset
ptrOffset = offset
case encoder.OpMarshalJSONPtr:
p := load(ctxptr, code.Idx)
if p == 0 {
Expand Down Expand Up @@ -496,7 +533,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
storeIndent(ctxptr, c.End.Next, uintptr(oldBaseIndent))
code = c
recursiveLevel++
case encoder.OpRecursiveEnd:
Expand Down
15 changes: 11 additions & 4 deletions internal/encoder/compiler.go
Expand Up @@ -78,12 +78,19 @@ func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
}
noescapeKeyCode = copyOpcode(noescapeKeyCode)
escapeKeyCode = copyOpcode(escapeKeyCode)
setTotalLengthToInterfaceOp(noescapeKeyCode)
setTotalLengthToInterfaceOp(escapeKeyCode)
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
codeLength := noescapeKeyCode.TotalLength()
codeSet := &OpcodeSet{
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
storeOpcodeSet(typeptr, codeSet, opcodeMap)
return codeSet, nil
Expand Down
15 changes: 11 additions & 4 deletions internal/encoder/compiler_norace.go
Expand Up @@ -37,12 +37,19 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
}
noescapeKeyCode = copyOpcode(noescapeKeyCode)
escapeKeyCode = copyOpcode(escapeKeyCode)
setTotalLengthToInterfaceOp(noescapeKeyCode)
setTotalLengthToInterfaceOp(escapeKeyCode)
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
codeLength := noescapeKeyCode.TotalLength()
codeSet := &OpcodeSet{
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
cachedOpcodeSets[index] = codeSet
return codeSet, nil
Expand Down
15 changes: 11 additions & 4 deletions internal/encoder/compiler_race.go
Expand Up @@ -44,12 +44,19 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {

noescapeKeyCode = copyOpcode(noescapeKeyCode)
escapeKeyCode = copyOpcode(escapeKeyCode)
setTotalLengthToInterfaceOp(noescapeKeyCode)
setTotalLengthToInterfaceOp(escapeKeyCode)
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
codeLength := noescapeKeyCode.TotalLength()
codeSet := &OpcodeSet{
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
setsMu.Lock()
cachedOpcodeSets[index] = codeSet
Expand Down
11 changes: 7 additions & 4 deletions internal/encoder/encoder.go
Expand Up @@ -94,10 +94,13 @@ func (t OpType) IsMultipleOpField() bool {
}

type OpcodeSet struct {
Type *runtime.Type
NoescapeKeyCode *Opcode
EscapeKeyCode *Opcode
CodeLength int
Type *runtime.Type
NoescapeKeyCode *Opcode
EscapeKeyCode *Opcode
InterfaceNoescapeKeyCode *Opcode
InterfaceEscapeKeyCode *Opcode
CodeLength int
EndCode *Opcode
}

type CompiledCode struct {
Expand Down
49 changes: 47 additions & 2 deletions internal/encoder/opcode.go
Expand Up @@ -285,6 +285,46 @@ func copyOpcode(code *Opcode) *Opcode {
return code.copy(codeMap)
}

func setTotalLengthToInterfaceOp(code *Opcode) {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
if c.Op == OpInterface {
c.Length = uint32(code.TotalLength())
}
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
}

func ToEndCode(code *Opcode) *Opcode {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
return c
}

func copyToInterfaceOpcode(code *Opcode) *Opcode {
copied := copyOpcode(code)
c := copied
c = ToEndCode(c)
copied.Op = copied.Op.PtrHeadToHead()
c.Idx += uintptrSize
c.ElemIdx = c.Idx + uintptrSize
c.Length = c.Idx + 2*uintptrSize
c.Op = OpInterfaceEnd
return copied
}

func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
return &Opcode{
Op: op,
Expand Down Expand Up @@ -354,7 +394,8 @@ func (c *Opcode) BeforeLastCode() *Opcode {

func (c *Opcode) TotalLength() int {
var idx int
for code := c; code.Op != OpEnd; {
code := c
for code.Op != OpEnd && code.Op != OpInterfaceEnd {
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
Expand All @@ -369,6 +410,10 @@ func (c *Opcode) TotalLength() int {
code = code.Next
}
}
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
return idx + 1
}

Expand Down Expand Up @@ -508,7 +553,7 @@ func (c *Opcode) dumpValue(code *Opcode) string {

func (c *Opcode) Dump() string {
codes := []string{}
for code := c; code.Op != OpEnd; {
for code := c; code.Op != OpEnd && code.Op != OpInterfaceEnd; {
switch code.Op.CodeType() {
case CodeSliceHead:
codes = append(codes, c.dumpHead(code))
Expand Down

0 comments on commit 3829400

Please sign in to comment.