Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve encoding performance of empty interface type #259

Merged
merged 3 commits into from Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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