【Go语言测评】三大json库性能对比encoding/json、sonic、easyjson

2,701 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

序列化与反序列化

  • 序列化:将结构体转为string
  • 反序列化:将string转为结构体

为什么要序列化?

后端多机数据交换时为字符串,需要将数据转化为字符串的形式,类似xml、html等,Go语言中发送方只要把结构体数据转化为字符串,接收方收到解码就可得到原始数据格式。

主要的json库

  • go语言自带的encoding/json:
    • 使用简单、速度感人
  • easyjson:
    • 第三方开源库
    • 使用时要先生成对应结构体的操作代码,使用起来比较麻烦
    • 速度快
  • sonic
    • 字节跳动技术团队开源
    • 据说一些场景下比easyjson还快很多
    • 使用方便与官网库无缝衔接,0学习成本

测试1:简单结构体

type BasicInfo struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}
type JobInfo struct {
	Skills []string `json:"skills"`
}
type Employee struct {
	BasicInfo BasicInfo `json:"basic_info"`
	JobInfo   JobInfo   `json:"job_info"`
}
var jsonStr = `{
	"basic_info":{
	  	"name":"Mike",
		"age":30
	},
	"job_info":{
		"skills":["Java","Go","C"]
	}
}	`

测试代码

利用go语言自带的benchmark工具进行分析

func Benchmark_json_unma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		json.Unmarshal([]byte(jsonStr), e)
	}
}

func Benchmark_json_ma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		json.Marshal(e)
	}
}

func Benchmark_sonic_unma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		sonic.Unmarshal([]byte(jsonStr), e)
	}
}

func Benchmark_sonic_ma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		sonic.Marshal(e)
	}
}

func Benchmark_easyjson_unma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		e.UnmarshalJSON([]byte(jsonStr))
	}
}

func Benchmark_easyjson_ma(b *testing.B) {
	e := new(Employee)
	for i := 0; i < b.N; i++ {
		e.MarshalJSON()
	}
}

测试结果

Amazing,easyjson在这样的简单数据下还是🐂,特别是Marshal场景下,比官方库整整快了一个数量级。

goos: windows
goarch: amd64
pkg: learn/basic/prof/easyjson
cpu: AMD Ryzen 7 6800H with Radeon Graphics
Benchmark_json_unma-16            939105              1279 ns/op
Benchmark_json_ma-16             2304142               502.1 ns/op
Benchmark_sonic_unma-16          1731814               689.0 ns/op
Benchmark_sonic_ma-16            2631068               451.1 ns/op
Benchmark_easyjson_unma-16       3035588               396.3 ns/op
Benchmark_easyjson_ma-16        10575062               106.6 ns/op
Marshal(ns/op)Unmarshal(ns/op)
encoding/json502.11279 ns/op
sonic451.1689.0
easyjson106.6396.3

测试2:

这一次测试一下不同长度数组的序列化能力,可以反映在不同数据量大小下的性能。

type Request struct {
	TransactionID string `json:"transaction_id"`
	PayLoad       []int  `json:"payload"`
}
func createStr(n int) string {
	payload := make([]int, n, n)
	for i := 0; i < n; i++ {
		payload[i] = i
	}
	req := Request{"demo_transaction", payload}
	v, err := json.Marshal(&req)
	if err != nil {
		panic(err)
	}
	return string(v)
}

测试结果

再次Amazing,对于序列化,sonic和官方库不相上下,easyjson继续吊打。反序列化数据量越大sonic优势越明显,但是还是比easyjson差一些。

Marshal100(ns/op)10000(ns/op)1000000(ns/op)
encoding/json354.5347.6342.3
sonic349.2353.0307.9
easyjson76.5177.2567.26
Unmarshal100(ns/op)10000(ns/op)1000000(ns/op)
encoding/json564663021576074340
sonic426044774945891464
easyjson262127361230164423

数组长度100

goos: windows
goarch: amd64
pkg: learn/basic/prof/demo2
cpu: AMD Ryzen 7 6800H with Radeon Graphics
Benchmark_json_unma-16            214905              5646 ns/op
Benchmark_json_ma-16             3302413               354.5 ns/op
Benchmark_sonic_unma-16           285255              4260 ns/op
Benchmark_sonic_ma-16            3484773               349.2 ns/op
Benchmark_easyjson_unma-16        443928              2621 ns/op
Benchmark_easyjson_ma-16        15375192                76.51 ns/op

数组长度10000

goos: windows
goarch: amd64
pkg: learn/basic/prof/demo2
cpu: AMD Ryzen 7 6800H with Radeon Graphics
Benchmark_json_unma-16              1893            630215 ns/op
Benchmark_json_ma-16             3482816               347.6 ns/op
Benchmark_sonic_unma-16             2642            447749 ns/op
Benchmark_sonic_ma-16            3509079               353.0 ns/op
Benchmark_easyjson_unma-16          4383            273612 ns/op
Benchmark_easyjson_ma-16        14885862                77.25 ns/op

数组长度1000000

goos: windows
goarch: amd64
pkg: learn/basic/prof/demo2
cpu: AMD Ryzen 7 6800H with Radeon Graphics
Benchmark_json_unma-16                15          76074340 ns/op
Benchmark_json_ma-16             3529484               342.3 ns/op
Benchmark_sonic_unma-16               25          45891464 ns/op
Benchmark_sonic_ma-16            3946147               307.9 ns/op
Benchmark_easyjson_unma-16            39          30164423 ns/op
Benchmark_easyjson_ma-16        16737871                67.26 ns/op

结论

结论想必不用多说大家也可以直观地看到。官方库性能较弱,sonic使用简单,在一些场景下性能不算突出,easyjson使用较复杂,但性能提升明显。