go json库的序列化、反序列化性能,api可用性对比调研。
go原生标准库,对go数据结构的json格式转换有很好的支持,但通过反射序列化/反序列化,性能是较差的。
https://github.com/mailru/easyjson
star:4.1K,最后的正式版本2021-02。
1、快,未使用反射。(所有说自己快的go json库都会和easyjson对比一下)
2、支持序列化/反序列化。
3、2年未更新版本。
4、使用时要先生成对应结构体的操作代码,丧失了 json 的灵活性,增加维护成本,使用起来比较麻烦。
easyjson的开发模式与protobuf类似,在程序运行之前需要使用其代码工具,为每一个结构体专门生成序列化/反序列化的程序代码。每一个程序都有定制化的解析函数。但也因为这种开发模式,easyjson对业务的侵入性比较高。一方面,在go build之前需要先生成代码;另一方面,相关的JSON处理函数也不兼容原生json库。
个人不推荐,类比protobuf。
https://github.com/goccy/go-json
与 Go 的 encoding/json 兼容的快速 JSON 编码器/解码器
star:2.2K,最后的正式版本2023-03。
1、支持序列化/反序列化。
2、兼容原生encoding/json。
3、追求快速,缓冲区重用、消除反射...等。
https://github.com/json-iterator/go
“encoding/json”的高性能 100% 兼容的替代品
star:12.1K,最后的正式版本2021-09。
1、支持序列化/反序列化。
2、兼容原生encoding/json。
https://github.com/tidwall/gjson
快速获取 JSON 值 - Go 的 JSON 解析器
star:12K,最后的正式版本2022-11.
1、提供了一种快速简单的方法来从 json 文档中获取值。
2、不兼容原生encoding/json。
https://github.com/bytedance/sonic
一个超快的 JSON 序列化和反序列化库,字节公司开源。
star:4.4K,最后的正式版本2023-04.
1、一个超快的 JSON 序列化和反序列化库,由 JIT(即时编译)和 SIMD(单指令多数据)加速。
2、无需代码生成的运行时对象绑定
3、用于 JSON 值操作的完整 API
4、快,快,快!
5、兼容原生encoding/json。
https://github.com/buger/jsonparser
不需要模式的 Go 最快的替代 JSON 解析器之一
star:5K,最后的正式版本2021-01。
1、不需要知道有效负载的结构(例如创建结构),并允许通过提供路径来访问字段。它比标准包快 10 倍encoding/json,不分配更多内存。
2、目标是将 JSON 解析器推向性能极限,而不是牺牲合规性和开发人员用户体验。
还有一些其他的go json库,由于长期未维护(更新久远)、star数相对较少等原因,例如:simplejson、ffjson、fastjson(go)等,未被纳入基准测试范围。
type User struct {
Id int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Age int64 `json:"age,omitempty"`
Desc string `json:"desc,omitempty"`
Address Address `json:"address"`
}
type Address struct {
Country string
Province string
City string
}
var yzh = User{Id: 1, Name: "zihe", Age: 18, Desc: "hello world",
Address: Address{Country: "中国", Province: "浙江", City: "你猜是不是杭州啊"},
}
4 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 141601 8562 ns/op 4196 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 133881 8844 ns/op 4195 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 126854 8758 ns/op 4195 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 823544 1451 ns/op 4664 B/op 5 allocs/op
Sonic速度快6-7倍,但是内存分配次数也较多。GoJson、JsonIter性能甚至不如原生Json。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 53494 22429 ns/op 4640 B/op 12 allocs/op
Benchmark_Json_GoJson-12 390686 2997 ns/op 4098 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 281121 4275 ns/op 4394 B/op 8 allocs/op
Benchmark_Json_Sonic-12 874654 1300 ns/op 4285 B/op 1 allocs/op
Benchmark_Json_GJson-12 1340902 907.1 ns/op 4096 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 1000000 1029 ns/op 4096 B/op 1 allocs/op
gjson、jsonparser胜出,比go原生json快20多倍。
62 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 9124 119715 ns/op 66063 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 8818 132278 ns/op 65907 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 8947 126720 ns/op 65930 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 88861 13543 ns/op 68981 B/op 5 allocs/op
Sonic速度快7-8倍,但是内存分配次数是2.5倍。GoJson、JsonIter性能甚至不如原生Json。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 3511 317132 ns/op 67360 B/op 12 allocs/op
Benchmark_Json_GoJson-12 28514 42155 ns/op 65573 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 20366 58975 ns/op 67148 B/op 8 allocs/op
Benchmark_Json_Sonic-12 111320 10951 ns/op 68335 B/op 1 allocs/op
Benchmark_Json_GJson-12 132273 9155 ns/op 65536 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 120890 9958 ns/op 65536 B/op 1 allocs/op
gjson胜出,比go原生json快30多倍。
480 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 1302 914762 ns/op 522505 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 1105 1050303 ns/op 514526 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 1214 988615 ns/op 509028 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 12532 84001 ns/op 505050 B/op 5 allocs/op
Sonic速度快10-11倍,但是内存分配次数是2.5倍。GoJson、JsonIter性能甚至不如原生Json。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 489 2374119 ns/op 491809 B/op 12 allocs/op
Benchmark_Json_GoJson-12 3666 307739 ns/op 491731 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 2730 443676 ns/op 491783 B/op 8 allocs/op
Benchmark_Json_Sonic-12 13660 75698 ns/op 503926 B/op 1 allocs/op
Benchmark_Json_GJson-12 19275 62513 ns/op 491520 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 17703 67776 ns/op 491520 B/op 1 allocs/op
gjson胜出,比go原生json快30多倍。
1.6 Mb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 380 3184574 ns/op 1740204 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 331 3532169 ns/op 1738670 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 363 3228532 ns/op 1723129 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 4981 239415 ns/op 1735739 B/op 5 allocs/op
Sonic速度快12-14倍,但是内存分配次数是2.5倍。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 144 8332906 ns/op 1704224 B/op 12 allocs/op
Benchmark_Json_GoJson-12 1011 1177656 ns/op 1704225 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 793 1556749 ns/op 1704368 B/op 8 allocs/op
Benchmark_Json_Sonic-12 4195 301555 ns/op 1728360 B/op 1 allocs/op
Benchmark_Json_GJson-12 10174 131980 ns/op 1703936 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 7316 183230 ns/op 1703942 B/op 1 allocs/op
gjson胜出,比go原生json快60多倍。
map[string]interface{},嵌套多层struct
var mapData = map[string]interface{}{
"name": "John",
"age": 30,
"city": yzh,
"addressBirth": Address{
Country: "中国",
Province: "河南",
City: "你猜是不是杭州啊"},
}
11 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 143587 8259 ns/op 4195 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 135042 8693 ns/op 4195 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 137720 8456 ns/op 4195 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 760065 1406 ns/op 4642 B/op 5 allocs/op
Sonic速度快5-6倍,但是内存分配次数也较多。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 20538 58220 ns/op 272 B/op 8 allocs/op
Benchmark_Json_GoJson-12 143998 8049 ns/op 12294 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 123408 9538 ns/op 72 B/op 12 allocs/op
Benchmark_Json_Sonic-12 395383 2853 ns/op 12889 B/op 2 allocs/op
Benchmark_Json_GJson-12 580441 2051 ns/op 12288 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 568903 2106 ns/op 12288 B/op 1 allocs/op
gjson、jsonparser速度胜出,比go原生json快20多倍。内存分配次数更少,但内存占用更多,一次把数据放到了内存缓存中。
70 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 9559 118677 ns/op 65900 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 8502 132664 ns/op 65900 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 9210 128783 ns/op 65908 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 89984 13488 ns/op 68870 B/op 5 allocs/op
Sonic速度快8-9倍,但是内存分配次数也较多。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 3346 343629 ns/op 272 B/op 8 allocs/op
Benchmark_Json_GoJson-12 25134 47629 ns/op 73770 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 21850 54679 ns/op 72 B/op 12 allocs/op
Benchmark_Json_Sonic-12 83118 12879 ns/op 76618 B/op 2 allocs/op
Benchmark_Json_GJson-12 123759 9612 ns/op 73728 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 116882 10245 ns/op 73728 B/op 1 allocs/op
gjson、jsonparser速度胜出,比go原生json快30多倍。内存分配次数更少,但内存占用更多,一次把数据放到了内存缓存中。
500 Kb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 1114 1023233 ns/op 505429 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 1095 1174343 ns/op 517592 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 1236 1184448 ns/op 508721 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 10578 130489 ns/op 504201 B/op 5 allocs/op
Sonic速度快7-9倍,但是内存分配次数也较多。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 496 2379973 ns/op 272 B/op 8 allocs/op
Benchmark_Json_GoJson-12 3532 308058 ns/op 499933 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 3140 381250 ns/op 72 B/op 12 allocs/op
Benchmark_Json_Sonic-12 15278 74342 ns/op 505365 B/op 2 allocs/op
Benchmark_Json_GJson-12 22629 53091 ns/op 499712 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 20624 57759 ns/op 499712 B/op 1 allocs/op
gjson、jsonparser速度胜出,比go原生json快40多倍。内存分配次数更少,但内存占用更多,一次把数据放到了内存缓存中。
1.6 Mb
序列化基准测试
Benchmark_Encode_SmallStruct_Json-12 241 5499286 ns/op 1713780 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_GoJson-12 224 5261783 ns/op 1724636 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_JsonIter-12 238 5024745 ns/op 1732964 B/op 2 allocs/op
Benchmark_Encode_SmallStruct_Sonic-12 2770 441430 ns/op 1743449 B/op 5 allocs/op
Sonic速度快12-13倍,但是内存分配次数也较多。
反序列化基准测试
Benchmark_Json_Unmarshal1-12 100 14351250 ns/op 272 B/op 8 allocs/op
Benchmark_Json_GoJson-12 853 1655101 ns/op 1712450 B/op 1 allocs/op
Benchmark_Json_JsonIter-12 667 2068097 ns/op 74 B/op 12 allocs/op
Benchmark_Json_Sonic-12 2359 441553 ns/op 1734026 B/op 2 allocs/op
Benchmark_Json_GJson-12 4999 223816 ns/op 1712129 B/op 1 allocs/op
Benchmark_Json_JsonParser-12 4827 298706 ns/op 1712133 B/op 1 allocs/op
gjson、jsonparser速度胜出,比go原生json快60多倍。内存分配次数更少,但内存占用更多,一次把数据放到了内存缓存中。
序列化:Sonic速度最快,且数据越大,速度差距越大,但Sonic的内存分配次数更多些,内存分配大小无明显差距。GoJson、JsonIter性能甚至不如原生encoding/json。
反序列化:gjson最快,jsonparser次之,Sonic紧随其后,比原生encoding/json快20-70倍,数据越大,速度差距越大。
序列化:Sonic速度最快,且数据越大,速度差距越大,但Sonic的内存分配次数更多些,内存分配大小无明显差距。在map value为interface,且interface中嵌套多层struct的情况下,GoJson、JsonIter性能甚至不如原生Json。
反序列化:gjson最快,jsonparser次之,Sonic紧随其后,比原生encoding/json快20-70倍,数据越大,速度差距越大。但内存占用更多,是一次把数据放到了内存缓存中,少cpu多内存。原生encoding/json、JsonIter则是分批处理,每次操作cpu多次进行内存分配,内存占用更少,多cpu低内存。
根据简单struct、嵌套struct、复杂map等类型的序列化、反序列化的基准测试,可得出:
1、序列化选择Sonic,速度在各场景都是最快的。
2、反序列化gjson最快,但gjson侧重的是从json中取值,不兼容原生encoding/json,可考虑Sonic,速度稍不如gjson,但兼容原生encoding/json。
嵌套struct:4K |
嵌套struct:62K |
嵌套struct:480K |
嵌套struct:1.6M |
复杂map:11K |
复杂map:70K |
复杂map:500K |
复杂map:1.6M |
|
encoding/json |
8562 ns/op |
119715 ns/op |
914762 ns/op |
3184574 ns/op |
8259 ns/op |
118677 ns/op |
1023233 ns/op |
5499286 ns/op |
go-json |
8844 ns/op |
132278 ns/op |
1050303 ns/op |
3532169 ns/op |
8693 ns/op |
132664 ns/op |
1174343 ns/op |
5261783 ns/op |
json-iterator |
8758 ns/op |
126720 ns/op |
988615 ns/op |
3228532 ns/op |
8456 ns/op |
128783 ns/op |
1184448 ns/op |
5024745 ns/op |
sonic |
1451 ns/op |
13543 ns/op |
84001 ns/op |
239415 ns/op |
1406 ns/op |
13488 ns/op |
130489 ns/op |
441430 ns/op |
嵌套struct:4K |
嵌套struct:62K |
嵌套struct:480K |
嵌套struct:1.6M |
复杂map:11K |
复杂map:70K |
复杂map:500K |
复杂map:1.6M |
|
encoding/json |
22429 ns/op |
317132 ns/op |
2374119 ns/op |
8332906 ns/op |
58220 ns/op |
343629 ns/op |
2379973 ns/op |
14351250 ns/op |
go-json |
2997 ns/op |
42155 ns/op |
307739 ns/op |
1177656 ns/op |
8049 ns/op |
47629 ns/op |
308058 ns/op |
1655101 ns/op |
json-iterator |
4275 ns/op |
58975 ns/op |
443676 ns/op |
1556749 ns/op |
9538 ns/op |
54679 ns/op |
381250 ns/op |
2068097 ns/op |
sonic |
1300 ns/op |
10951 ns/op |
75698 ns/op |
301555 ns/op |
2853 ns/op |
12879 ns/op |
74342 ns/op |
441553 ns/op |
gjson |
907.1 ns/op |
9155 ns/op |
62513 ns/op |
131980 ns/op |
2051 ns/op |
9612 ns/op |
53091 ns/op |
223816 ns/op |
jsonparser |
1029 ns/op |
9958 ns/op |
67776 ns/op |
183230 ns/op |
2106 ns/op |
10245 ns/op |
57759 ns/op |
298706 ns/op |
基准测试代码
package main
import (
"encoding/json"
"github.com/buger/jsonparser"
"github.com/bytedance/sonic"
gojson "github.com/goccy/go-json"
jsoniter "github.com/json-iterator/go"
"github.com/tidwall/gjson"
"testing"
)
// var yzhBytes, _ = json.Marshal(yzh)
var yzhBytes, _ = json.Marshal(mapData)
var yzh2 User
var yzhString2 = string(yzhBytes)
func Benchmark_Encode_SmallStruct_Json(b *testing.B) {
for i := 0; i < b.N; i++ {
json.Marshal(yzh)
}
}
func Benchmark_Encode_SmallStruct_GoJson(b *testing.B) {
for i := 0; i < b.N; i++ {
gojson.Marshal(yzh)
}
}
var JsonIter = jsoniter.ConfigCompatibleWithStandardLibrary
func Benchmark_Encode_SmallStruct_JsonIter(b *testing.B) {
for i := 0; i < b.N; i++ {
JsonIter.Marshal(yzh)
}
}
func Benchmark_Encode_SmallStruct_Sonic(b *testing.B) {
for i := 0; i < b.N; i++ {
sonic.Marshal(yzh)
}
}
func Benchmark_Json_Unmarshal1(b *testing.B) {
for i := 0; i < b.N; i++ {
json.Unmarshal(yzhBytes, &yzh2)
}
}
func Benchmark_Json_GoJson(b *testing.B) {
for i := 0; i < b.N; i++ {
gojson.Unmarshal(yzhBytes, &yzh2)
}
}
var JsonIter2 = jsoniter.ConfigCompatibleWithStandardLibrary
func Benchmark_Json_JsonIter(b *testing.B) {
for i := 0; i < b.N; i++ {
JsonIter2.Unmarshal(yzhBytes, &yzh2)
}
}
func Benchmark_Json_Sonic(b *testing.B) {
for i := 0; i < b.N; i++ {
sonic.Unmarshal(yzhBytes, &yzh2)
}
}
var gjsonString string
func Benchmark_Json_GJson(b *testing.B) {
for i := 0; i < b.N; i++ {
gjsonString = gjson.ParseBytes(yzhBytes).Raw
}
}
var jsonparserString string
func Benchmark_Json_JsonParser(b *testing.B) {
for i := 0; i < b.N; i++ {
jsonparserString, _ = jsonparser.ParseString(yzhBytes)
}
}
//func Benchmark_Json_(b *testing.B) {
// //for i := 0; i < b.N; i++ {
// for i := 0; i < 1; i++ {
// fmt.Println(yzh2)
// json.Unmarshal(yzhBytes, &yzh2)
// fmt.Println(yzh2)
// }
//}
//func Benchmark_Json_test(b *testing.B) {
// var s = yzhString2
// fmt.Println(len(s))
//}
//func Benchmark_Json_test2(b *testing.B) {
// bytes, _ := sonic.Marshal(yzh)
// sonic.Unmarshal(bytes, &yzh2)
// result := gjson.ParseBytes(bytes)
// fmt.Println(yzh2)
// fmt.Println(result.Raw)
//}