主要分析下面2个json库,判断他们的性能差异

① github.com/valyala/fastjson
② github.com/bitly/go-simplejson

假设我们要解码的字符串都是二维数组类型,业务需求是读取二维数组中的某个元素。二维数组的example如下,这样的编码方式主要为了节省存储空间,省去了存储 key 的额外开销。我们要读取内容中的 china 值,也就是第一个数组中的第二个元素:

[["2", "china", "10", "55", "1", "text", "0", "0", "0", "11ms", "cpu"],[""]]

使用 fastjson 读取 china 的代码实现:

func FastJson(str string) string {
	json, _ := fastjson.Parse(str)
	arrs, _ := json.Array()
	for _, elem := range arrs {
		sub, _ := elem.Array()
		result, _ := sub[1].StringBytes()
		return string(result)
	}

	return ""
}

使用simplejson读取china 的代码实现:

func SimpleJson(str string) string {
	json, _ := simplejson.NewJson([]byte(str))
	arrs, _ := json.Array()
	for _, elem := range arrs {
		sub, _ := elem.([]interface{})
		return sub[1].(string)
	}

	return ""
}

编写benchmark测试用例,分析两个方法的性能,测试用例如下:

import "testing"

var (
	str   = `[["2", "china", "10", "55", "1", "text", "0", "0", "0", "11ms", "cpu"],[""]]`
	china string
)

func BenchmarkFastJson(b *testing.B) {
	for i := 0; i < b.N; i++ {
		china = FastJson(str)
	}
}

func BenchmarkSimpleJson(b *testing.B) {
	for i := 0; i < b.N; i++ {
		china = SimpleJson(str)
	}
}

每个方法执行5次,控制台查看benchmark的分析结果,粗略统计,fastjson是simplejson性能的2倍。通过 benchstat 我们还可以获得性能的分析结果

go test -bench=. -benchmem -count=5 . | tee p.txt 

在这里插入图片描述
在使用 benchstat之前需要先安装这个工具,它分析的数据源就是我们 bench 执行的输出结果,全部导出到了文件 p.txt中。

go get -u golang.org/x/perf/cmd/benchstat

使用 benchstat 来查看性能的分析结果

benchstat p.txt

通过分析结果,我们可以清楚的看到两个函数的性能差异,单次执行的耗时、申请内存次数、内存大小的比较

在这里插入图片描述

fastjson文档说,比原生的json快了 15x 的性能,这还远远没有达到呢。其实fastjson提供了sync.Pool的功能,我们在FastJson方法的基础上应用对象池来看看性能的变化。

另外,我们要比较fastjson使用sync.Pool和不使用sync.Pool的两种方式。观察一下两者的性能差异。先将当前的go test 执行程序保存下来

# 该例子中不需要保持,删除这段
# go test -c 
# 重命名 *.test 为 old.text

修改代码,fastjson使用sync.Pool的处理方式,将FastJson修改为如下,通过ParserPool声明一个对象池,然后每次执行解析的时候从池中获取一个parser对象,执行完成之后,放回去

var fastjsonPool = fastjson.ParserPool{}

func FastJson(str string) string {
	parser := fastjsonPool.Get()
	defer fastjsonPool.Put(parser)

	json, _ := parser.Parse(str)
	arrs, _ := json.Array()
	for _, elem := range arrs {
		sub, _ := elem.Array()
		result, _ := sub[1].StringBytes()
		return string(result)
	}

	return ""
}

我们将这次benchmark的执行结果保存到new.txt中,重新执行benchmark

 go test -bench=. -benchmem -count=5 . | tee new.txt 

通过 benchstat 比较两次的性能差异

benchstat p.txt new.txt 

我们看FastJson old 和 new 的比较,性能提升了6倍

在这里插入图片描述