From 95b2194742b86054cdb069f22afa05da3e2e00b8 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 11:56:56 +0900 Subject: [PATCH] Add optimized code --- encode.go | 4 + encode_compile.go | 127 +++++++++++++++----- encode_opcode.go | 111 +++++++++++++++++- encode_vm.go | 288 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 492 insertions(+), 38 deletions(-) diff --git a/encode.go b/encode.go index 4e7cb15..8f7cb17 100644 --- a/encode.go +++ b/encode.go @@ -163,6 +163,10 @@ func (e *Encoder) encodeBool(v bool) { e.buf = strconv.AppendBool(e.buf, v) } +func (e *Encoder) encodeBytes(b []byte) { + e.buf = append(e.buf, b...) +} + func (e *Encoder) encodeString(s string) { b := *(*[]byte)(unsafe.Pointer(&s)) e.buf = append(e.buf, b...) diff --git a/encode_compile.go b/encode_compile.go index 83f89b9..b4c3102 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -53,22 +53,50 @@ func (e *Encoder) compileOp(typ *rtype) (*opcode, error) { return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) } -func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { - code, err := e.compileOp(typ.Elem()) - if err != nil { - return nil, err - } +func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode { switch code.op { case opStructFieldHead: code.op = opStructFieldPtrHead case opStructFieldHeadInt: code.op = opStructFieldPtrHeadInt + case opStructFieldHeadInt8: + code.op = opStructFieldPtrHeadInt8 + case opStructFieldHeadInt16: + code.op = opStructFieldPtrHeadInt16 + case opStructFieldHeadInt32: + code.op = opStructFieldPtrHeadInt32 + case opStructFieldHeadInt64: + code.op = opStructFieldPtrHeadInt64 + case opStructFieldHeadUint: + code.op = opStructFieldPtrHeadUint + case opStructFieldHeadUint8: + code.op = opStructFieldPtrHeadUint8 + case opStructFieldHeadUint16: + code.op = opStructFieldPtrHeadUint16 + case opStructFieldHeadUint32: + code.op = opStructFieldPtrHeadUint32 + case opStructFieldHeadUint64: + code.op = opStructFieldPtrHeadUint64 + case opStructFieldHeadFloat32: + code.op = opStructFieldPtrHeadFloat32 + case opStructFieldHeadFloat64: + code.op = opStructFieldPtrHeadFloat64 case opStructFieldHeadString: code.op = opStructFieldPtrHeadString + case opStructFieldHeadBool: + code.op = opStructFieldPtrHeadBool default: - return newOpCode(opPtr, typ, code), nil + return newOpCode(opPtr, typ, code) } - return code, nil + return code +} + +func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { + code, err := e.compileOp(typ.Elem()) + if err != nil { + return nil, err + } + return e.optimizeStructFieldPtrHead(typ, code), nil } func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) { @@ -154,9 +182,9 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) { } func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { - // header => firstField => structField => end - // ^ | - // |________| + // header => code => structField => code => end + // ^ | + // |__________| fieldNum := typ.NumField() fieldIdx := 0 var ( @@ -183,47 +211,86 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { return nil, err } key := fmt.Sprintf(`"%s":`, keyName) + fieldCode := &structFieldCode{ + opcodeHeader: &opcodeHeader{ + typ: fieldType, + next: valueCode, + }, + key: []byte(key), + offset: field.Offset, + } if fieldIdx == 0 { - fieldCode := &structFieldCode{ - opcodeHeader: &opcodeHeader{ - op: opStructFieldHead, - typ: fieldType, - next: valueCode, - }, - key: key, - offset: field.Offset, - } + fieldCode.op = opStructFieldHead head = fieldCode code = (*opcode)(unsafe.Pointer(fieldCode)) prevField = fieldCode switch valueCode.op { case opInt: fieldCode.op = opStructFieldHeadInt + case opInt8: + fieldCode.op = opStructFieldHeadInt8 + case opInt16: + fieldCode.op = opStructFieldHeadInt16 + case opInt32: + fieldCode.op = opStructFieldHeadInt32 + case opInt64: + fieldCode.op = opStructFieldHeadInt64 + case opUint: + fieldCode.op = opStructFieldHeadUint + case opUint8: + fieldCode.op = opStructFieldHeadUint8 + case opUint16: + fieldCode.op = opStructFieldHeadUint16 + case opUint32: + fieldCode.op = opStructFieldHeadUint32 + case opUint64: + fieldCode.op = opStructFieldHeadUint64 + case opFloat32: + fieldCode.op = opStructFieldHeadFloat32 + case opFloat64: + fieldCode.op = opStructFieldHeadFloat64 case opString: fieldCode.op = opStructFieldHeadString + case opBool: + fieldCode.op = opStructFieldHeadBool default: code = valueCode.beforeLastCode() } } else { - fieldCode := &structFieldCode{ - opcodeHeader: &opcodeHeader{ - op: opStructField, - typ: fieldType, - next: valueCode, - }, - key: key, - offset: field.Offset, - } + fieldCode.op = opStructField code.next = (*opcode)(unsafe.Pointer(fieldCode)) prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode)) prevField = fieldCode + code = (*opcode)(unsafe.Pointer(fieldCode)) switch valueCode.op { case opInt: fieldCode.op = opStructFieldInt - code = (*opcode)(unsafe.Pointer(fieldCode)) + case opInt8: + fieldCode.op = opStructFieldInt8 + case opInt16: + fieldCode.op = opStructFieldInt16 + case opInt32: + fieldCode.op = opStructFieldInt32 + case opInt64: + fieldCode.op = opStructFieldInt64 + case opUint: + fieldCode.op = opStructFieldUint + case opUint8: + fieldCode.op = opStructFieldUint8 + case opUint16: + fieldCode.op = opStructFieldUint16 + case opUint32: + fieldCode.op = opStructFieldUint32 + case opUint64: + fieldCode.op = opStructFieldUint64 + case opFloat32: + fieldCode.op = opStructFieldFloat32 + case opFloat64: + fieldCode.op = opStructFieldFloat64 case opString: fieldCode.op = opStructFieldString - code = (*opcode)(unsafe.Pointer(fieldCode)) + case opBool: + fieldCode.op = opStructFieldBool default: code = valueCode.beforeLastCode() } diff --git a/encode_opcode.go b/encode_opcode.go index 81a789c..a7a5cb4 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -31,13 +31,49 @@ const ( opSliceEnd opStructFieldHead opStructFieldHeadInt + opStructFieldHeadInt8 + opStructFieldHeadInt16 + opStructFieldHeadInt32 + opStructFieldHeadInt64 + opStructFieldHeadUint + opStructFieldHeadUint8 + opStructFieldHeadUint16 + opStructFieldHeadUint32 + opStructFieldHeadUint64 + opStructFieldHeadFloat32 + opStructFieldHeadFloat64 opStructFieldHeadString + opStructFieldHeadBool opStructFieldPtrHead opStructFieldPtrHeadInt + opStructFieldPtrHeadInt8 + opStructFieldPtrHeadInt16 + opStructFieldPtrHeadInt32 + opStructFieldPtrHeadInt64 + opStructFieldPtrHeadUint + opStructFieldPtrHeadUint8 + opStructFieldPtrHeadUint16 + opStructFieldPtrHeadUint32 + opStructFieldPtrHeadUint64 + opStructFieldPtrHeadFloat32 + opStructFieldPtrHeadFloat64 opStructFieldPtrHeadString + opStructFieldPtrHeadBool opStructField opStructFieldInt + opStructFieldInt8 + opStructFieldInt16 + opStructFieldInt32 + opStructFieldInt64 + opStructFieldUint + opStructFieldUint8 + opStructFieldUint16 + opStructFieldUint32 + opStructFieldUint64 + opStructFieldFloat32 + opStructFieldFloat64 opStructFieldString + opStructFieldBool opStructEnd ) @@ -85,20 +121,92 @@ func (t opType) String() string { return "STRUCT_FIELD_HEAD" case opStructFieldHeadInt: return "STRUCT_FIELD_HEAD_INT" + case opStructFieldHeadInt8: + return "STRUCT_FIELD_HEAD_INT8" + case opStructFieldHeadInt16: + return "STRUCT_FIELD_HEAD_INT16" + case opStructFieldHeadInt32: + return "STRUCT_FIELD_HEAD_INT32" + case opStructFieldHeadInt64: + return "STRUCT_FIELD_HEAD_INT64" + case opStructFieldHeadUint: + return "STRUCT_FIELD_HEAD_UINT" + case opStructFieldHeadUint8: + return "STRUCT_FIELD_HEAD_UINT8" + case opStructFieldHeadUint16: + return "STRUCT_FIELD_HEAD_UINT16" + case opStructFieldHeadUint32: + return "STRUCT_FIELD_HEAD_UINT32" + case opStructFieldHeadUint64: + return "STRUCT_FIELD_HEAD_UINT64" + case opStructFieldHeadFloat32: + return "STRUCT_FIELD_HEAD_FLOAT32" + case opStructFieldHeadFloat64: + return "STRUCT_FIELD_HEAD_FLOAT64" case opStructFieldHeadString: return "STRUCT_FIELD_HEAD_STRING" + case opStructFieldHeadBool: + return "STRUCT_FIELD_HEAD_BOOL" case opStructFieldPtrHead: return "STRUCT_FIELD_PTR_HEAD" case opStructFieldPtrHeadInt: return "STRUCT_FIELD_PTR_HEAD_INT" + case opStructFieldPtrHeadInt8: + return "STRUCT_FIELD_PTR_HEAD_INT8" + case opStructFieldPtrHeadInt16: + return "STRUCT_FIELD_PTR_HEAD_INT16" + case opStructFieldPtrHeadInt32: + return "STRUCT_FIELD_PTR_HEAD_INT32" + case opStructFieldPtrHeadInt64: + return "STRUCT_FIELD_PTR_HEAD_INT64" + case opStructFieldPtrHeadUint: + return "STRUCT_FIELD_PTR_HEAD_UINT" + case opStructFieldPtrHeadUint8: + return "STRUCT_FIELD_PTR_HEAD_UINT8" + case opStructFieldPtrHeadUint16: + return "STRUCT_FIELD_PTR_HEAD_UINT16" + case opStructFieldPtrHeadUint32: + return "STRUCT_FIELD_PTR_HEAD_UINT32" + case opStructFieldPtrHeadUint64: + return "STRUCT_FIELD_PTR_HEAD_UINT64" + case opStructFieldPtrHeadFloat32: + return "STRUCT_FIELD_PTR_HEAD_FLOAT32" + case opStructFieldPtrHeadFloat64: + return "STRUCT_FIELD_PTR_HEAD_FLOAT64" case opStructFieldPtrHeadString: return "STRUCT_FIELD_PTR_HEAD_STRING" + case opStructFieldPtrHeadBool: + return "STRUCT_FIELD_PTR_HEAD_BOOL" case opStructField: return "STRUCT_FIELD" case opStructFieldInt: return "STRUCT_FIELD_INT" + case opStructFieldInt8: + return "STRUCT_FIELD_INT8" + case opStructFieldInt16: + return "STRUCT_FIELD_INT16" + case opStructFieldInt32: + return "STRUCT_FIELD_INT32" + case opStructFieldInt64: + return "STRUCT_FIELD_INT64" + case opStructFieldUint: + return "STRUCT_FIELD_UINT" + case opStructFieldUint8: + return "STRUCT_FIELD_UINT8" + case opStructFieldUint16: + return "STRUCT_FIELD_UINT16" + case opStructFieldUint32: + return "STRUCT_FIELD_UINT32" + case opStructFieldUint64: + return "STRUCT_FIELD_UINT64" + case opStructFieldFloat32: + return "STRUCT_FIELD_FLOAT32" + case opStructFieldFloat64: + return "STRUCT_FIELD_FLOAT64" case opStructFieldString: return "STRUCT_FIELD_STRING" + case opStructFieldBool: + return "STRUCT_FIELD_BOOL" case opStructEnd: return "STRUCT_END" } @@ -192,7 +300,6 @@ type sliceElemCode struct { len uintptr size uintptr data uintptr - elem *sliceElemCode // first => elem end *opcode } @@ -204,7 +311,7 @@ func (c *sliceElemCode) set(header *reflect.SliceHeader) { type structFieldCode struct { *opcodeHeader - key string + key []byte offset uintptr nextField *opcode end *opcode diff --git a/encode_vm.go b/encode_vm.go index c122b36..93df834 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -98,7 +98,7 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) code = field.next code.ptr = field.ptr + field.offset field.nextField.ptr = field.ptr @@ -114,11 +114,187 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) e.encodeInt(e.ptrToInt(field.ptr + field.offset)) field.nextField.ptr = field.ptr code = field.next } + case opStructFieldPtrHeadInt8: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt8: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt8(e.ptrToInt8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt16: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt16: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt16(e.ptrToInt16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt32(e.ptrToInt32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt64(e.ptrToInt64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint(e.ptrToUint(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint8: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint8: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint8(e.ptrToUint8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint16: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint16: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint16(e.ptrToUint16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint32(e.ptrToUint32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint64(e.ptrToUint64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeFloat32(e.ptrToFloat32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeFloat64(e.ptrToFloat64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } case opStructFieldPtrHeadString: code.ptr = e.ptrToPtr(code.ptr) fallthrough @@ -130,15 +306,31 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) e.encodeEscapedString(e.ptrToString(field.ptr + field.offset)) field.nextField.ptr = field.ptr code = field.next } + case opStructFieldPtrHeadBool: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadBool: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeBool(e.ptrToBool(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } case opStructField: e.encodeByte(',') c := code.toStructFieldCode() - e.encodeString(c.key) + e.encodeBytes(c.key) code = code.next code.ptr = c.ptr + c.offset c.nextField.ptr = c.ptr @@ -146,16 +338,100 @@ func (e *Encoder) run(code *opcode) error { e.encodeByte(',') c := code.toStructFieldCode() c.nextField.ptr = c.ptr - e.encodeString(c.key) + e.encodeBytes(c.key) e.encodeInt(e.ptrToInt(c.ptr + c.offset)) code = code.next + case opStructFieldInt8: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt8(e.ptrToInt8(c.ptr + c.offset)) + code = code.next + case opStructFieldInt16: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt16(e.ptrToInt16(c.ptr + c.offset)) + code = code.next + case opStructFieldInt32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt32(e.ptrToInt32(c.ptr + c.offset)) + code = code.next + case opStructFieldInt64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt64(e.ptrToInt64(c.ptr + c.offset)) + code = code.next + case opStructFieldUint: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint(e.ptrToUint(c.ptr + c.offset)) + code = code.next + case opStructFieldUint8: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint8(e.ptrToUint8(c.ptr + c.offset)) + code = code.next + case opStructFieldUint16: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint16(e.ptrToUint16(c.ptr + c.offset)) + code = code.next + case opStructFieldUint32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint32(e.ptrToUint32(c.ptr + c.offset)) + code = code.next + case opStructFieldUint64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint64(e.ptrToUint64(c.ptr + c.offset)) + code = code.next + case opStructFieldFloat32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeFloat32(e.ptrToFloat32(c.ptr + c.offset)) + code = code.next + case opStructFieldFloat64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeFloat64(e.ptrToFloat64(c.ptr + c.offset)) + code = code.next case opStructFieldString: e.encodeByte(',') c := code.toStructFieldCode() c.nextField.ptr = c.ptr - e.encodeString(c.key) + e.encodeBytes(c.key) e.encodeEscapedString(e.ptrToString(c.ptr + c.offset)) code = code.next + case opStructFieldBool: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeBool(e.ptrToBool(c.ptr + c.offset)) + code = code.next case opStructEnd: e.encodeByte('}') code = code.next