开启掘金成长之旅!这是我参与「掘金日新计划 · 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/json | 502.1 | 1279 ns/op |
sonic | 451.1 | 689.0 |
easyjson | 106.6 | 396.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差一些。
Marshal | 100(ns/op) | 10000(ns/op) | 1000000(ns/op) |
---|---|---|---|
encoding/json | 354.5 | 347.6 | 342.3 |
sonic | 349.2 | 353.0 | 307.9 |
easyjson | 76.51 | 77.25 | 67.26 |
Unmarshal | 100(ns/op) | 10000(ns/op) | 1000000(ns/op) |
---|---|---|---|
encoding/json | 5646 | 630215 | 76074340 |
sonic | 4260 | 447749 | 45891464 |
easyjson | 2621 | 273612 | 30164423 |
数组长度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使用较复杂,但性能提升明显。