首先说下问题吧,因为项目里面底层大量数据都是json 存储的,所以在程序里面就必须要用json序列化和反序列化。但是因为golang 里面的 标准库encoding/json cpu 占用很高(大量用到反射),所以寻找到了替代品 jsonitor (jsonitor 也用了反射,但是性能在我们的数据测试下 好了差不多一倍), 这次就是写篇总结看下 俩者有啥区别。
encoding/json vs json-iterator
主要性能diff 在 decode
在特定数据结构下, json 官方慢了接近3倍
BenchmarkDecodeStdStructMedium-8 5259856 222.1 ns/op 280 B/op 7 allocs/op BenchmarkDecodeJsoniterStructMedium-8 1371364 829.8 ns/op 696 B/op 15 allocs/op
看了下cpu profile 发现原生std json checkValid 检查结构体的正确性 站到了28%
在嵌套多一点的数据接口, json官方 快了2倍
checkValid 检查结构体的正确性 40%
encoding/json
func Unmarshal(data []byte, v any) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState
// checkVaild 就是用scanner 一个扫描器 每个byte 检查格式。
比如 reset 之后检查下一个byte 如果是
err := checkValid(data, &d.scan)
if err != nil {
return err
}
// 经过校验之后 , 初始化数据。 就是一些简单的赋值
d.init(data)
// unmarshal 将字符串流 转化成结构体。
err = d.unmarshal(v)
return err
}
// checkVaild.的核心就是一个 scanner 遍历 data 字节流 每个 byte 运行检查的step 函数检查 状态。
func checkValid(data []byte, scan *scanner) error {
scan.reset()
for _, c := range data {
scan.bytes++
if scan.step(scan, c) == scanError {
return scan.err
}
}
if scan.eof() == scanError {
return scan.err
}
return nil
}
// scan 的step 函数初始化 stateBeginValue 根据不同byte 选择对应的校验函数。 在不同的数据 比如{} 和 []
// 用到了 一个pushParseState function 加了一个入栈队列 入栈 检查 对应闭合符号。
func stateBeginValue(s *scanner, c byte) int {
if isSpace(c) {
return scanSkipSpace
}
switch c {
case '{':
s.step = stateBeginStringOrEmpty
return s.pushParseState(c, parseObjectKey, scanBeginObject)
case '[':
s.step = stateBeginValueOrEmpty
return s.pushParseState(c, parseArrayValue, scanBeginArray)
case '"':
s.step = stateInString
return scanBeginLiteral
case '-':
s.step = stateNeg
return scanBeginLiteral
case '0': // beginning of 0.123
s.step = state0
return scanBeginLiteral
case 't': // beginning of true
s.step = stateT
return scanBeginLiteral
case 'f': // beginning of false
s.step = stateF
return scanBeginLiteral
case 'n': // beginning of null
s.step = stateN
return scanBeginLiteral
}
if '1' <= c && c <= '9' { // beginning of 1234.5
s.step = state1
return scanBeginLiteral
}
return s.error(c, "looking for beginning of value")
}
// decodeState represents the state while decoding a JSON value.
type decodeState struct {
data []byte
off int // next read offset in data
opcode int // last read result
scan scanner
errorContext *errorContext
savedError error
useNumber bool
disallowUnknownFields bool
}
json-iterator
// json-iterator 主要优化点在于, 以前reflect 反射 类型检查,内存分配 。
// 1. 现在 做了一堆decoder 对于用过的类型,就不用再进行内存分配了。
// 2. reflect2 减少了类型检查 为了提高反射的性能,reflect2使用了一种称为“类型缓存”的技术。
// 在Go语言中,每一个类型都对应着一个reflect.Type对象,reflect2通过将这些reflect.Type对象缓存起来,可以避免反复进行类型检查。
func _createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder {
decoder := createDecoderOfJsonRawMessage(ctx, typ)
if decoder != nil {
return decoder
}
decoder = createDecoderOfJsonNumber(ctx, typ)
if decoder != nil {
return decoder
}
decoder = createDecoderOfMarshaler(ctx, typ)
if decoder != nil {
return decoder
}
decoder = createDecoderOfAny(ctx, typ)
if decoder != nil {
return decoder
}
decoder = createDecoderOfNative(ctx, typ)
if decoder != nil {
return decoder
}
switch typ.Kind() {
case reflect.Interface:
ifaceType, isIFace := typ.(*reflect2.UnsafeIFaceType)
if isIFace {
return &ifaceDecoder{valType: ifaceType}
}
return &efaceDecoder{}
case reflect.Struct:
return decoderOfStruct(ctx, typ)
case reflect.Array:
return decoderOfArray(ctx, typ)
case reflect.Slice:
return decoderOfSlice(ctx, typ)
case reflect.Map:
return decoderOfMap(ctx, typ)
case reflect.Ptr:
return decoderOfOptional(ctx, typ)
default:
return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())}
}
}