比较下golang 的标准json 包 和 json-iterator 的性能diff

首先说下问题吧,因为项目里面底层大量数据都是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())}
	}
}