再測Golang的JSON庫
寫專案一直需要進行序列化,聽到了,也看到了很多同學老師對各個golang的json
庫進行測評。那本人為什麼還要繼續進行這一次測評呢?
因為實踐過的知識最有說服力,也是屬於自己的,我也希望看到本博文的同學老師可以修改和執行測評的程式碼執行一遍,我相信會有不一定的體會。
本次測評我選擇了類庫有:
類庫
序號 | 類庫 | 地址 | 備註 |
---|---|---|---|
1 | encoding/json | Golan | |
2 | easyjson | github.com/mailru/easyjson | |
3 | ffjson | github.com/mailru/easyjson | |
4 | iterator/json | github.com/json-iterator/go |
主要是針對上述的型別進行,本人採用了對不同的類庫使用不同的結構體(僅僅是結構體名稱不同,欄位順序和型別一樣)。
環境
環境為MacBook Pro(Core i5處理器/8GB記憶體)go1.8.3 darwin/amd64
程式碼
bench程式碼如下:
package jsonbench
import (
"encoding/gob"
"encoding/json"
"github.com/json-iterator/go"
"github.com/mailru/easyjson"
"github.com/pquerna/ffjson/ffjson"
"testing"
)
var (
iterator = jsoniter.ConfigCompatibleWithStandardLibrary
// easyjson
as = AgentService{
ServiceName: "kaleidoscope_api",
Version: "1517558949087295000_1298498081",
ServiceId: "kaleidoscope.com_v1.2",
Address: "127.0.0.1",
Port: 80,
Metadata: map[string]string{},
ConnectTimeOut: 1000,
ConnectType: "LONG",
ReadTimeOut: 1000,
WriteTimeOut: 1000,
Protocol: "HTTP",
Balance: "Random",
Idcs: "hu,hd,hn",
Converter: "json",
Retry: 3,
}
service = as.ToService()
asBytes, _ = json.Marshal(as)
serviceBytes, _ = json.Marshal(service)
asStr = string(asBytes)
serviceStr = string(serviceBytes)
asGonBytes, _ = GobEncode(as)
serviceGonBytes, _ = GobEncode(service)
// std
asstd = AgentServiceSTD{
ServiceName: "kaleidoscope_api",
Version: "1517558949087295000_1298498081",
ServiceId: "kaleidoscope.com_v1.2",
Address: "kaleidoscope.dev.igetget.com",
Port: 80,
Metadata: map[string]string{},
ConnectTimeOut: 1000,
ConnectType: "LONG",
ReadTimeOut: 1000,
WriteTimeOut: 1000,
Protocol: "HTTP",
Balance: "Random",
Idcs: "hu,hd,hn",
Converter: "json",
Retry: 3,
}
servicestd = asstd.ToServiceSTD()
asBytesstd, _ = json.Marshal(asstd)
serviceBytesstd, _ = json.Marshal(servicestd)
asStrstd = string(asBytesstd)
serviceStrstd = string(serviceBytesstd)
asGonBytesstd, _ = GobEncode(asstd)
serviceGonBytesstd, _ = GobEncode(servicestd)
)
// go test -bench=".*"
func init() {
gob.Register(AgentService{})
}
func Benchmark_STD_Marshal1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := json.Marshal(asstd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_STD_Marshal2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := json.Marshal(servicestd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_STD_Marshal1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := json.Marshal(as)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_STD_Marshal2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := json.Marshal(service)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_Marshal1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := easyjson.Marshal(as)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_Marshal2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := easyjson.Marshal(service)
if err != nil {
b.Error(err)
}
}
}
//
func Benchmark_ITERATOR_Marshal1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := iterator.Marshal(asstd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_ITERATOR_Marshal2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := iterator.Marshal(servicestd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_FFJSON_Marshal1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := ffjson.Marshal(asstd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_FFJSON_Marshal2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
_, err := ffjson.Marshal(servicestd)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_GOB_Encode1(b *testing.B) {
for i := 0; i < b.N*10; i++ {
as.Port = i
GobEncode(as)
}
}
func Benchmark_GOB_Encode2(b *testing.B) {
for i := 0; i < b.N*10; i++ {
GobEncode(service)
}
}
func Benchmark_STD_Unmarshal1(b *testing.B) {
tmp := AgentServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := json.Unmarshal(asBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_STD_Unmarshal2(b *testing.B) {
tmp := ServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := json.Unmarshal(serviceBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_STD_Unmarshal1(b *testing.B) {
tmp := AgentService{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := json.Unmarshal(asBytes, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_STD_Unmarshal2(b *testing.B) {
tmp := Service{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := json.Unmarshal(serviceBytes, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_Unmarshal1(b *testing.B) {
tmp := AgentService{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := easyjson.Unmarshal(asBytes, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_EASYJSON_Unmarshal2(b *testing.B) {
tmp := Service{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := easyjson.Unmarshal(serviceBytes, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_ITERATOR_UnMarshal1(b *testing.B) {
tmp := ServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := iterator.Unmarshal(serviceBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_ITERATOR_UnMarshal2(b *testing.B) {
tmp := ServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := iterator.Unmarshal(serviceBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_FFJSON_UnMarshal1(b *testing.B) {
tmp := ServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := ffjson.Unmarshal(serviceBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_FFJSON_UnMarshal2(b *testing.B) {
tmp := ServiceSTD{}
for i := 0; i < b.N*10; i++ {
as.Port = i
err := ffjson.Unmarshal(serviceBytesstd, &tmp)
if err != nil {
b.Error(err)
}
}
}
func Benchmark_GOB_Decode1(b *testing.B) {
tmp := AgentService{}
for i := 0; i < b.N*10; i++ {
as.Port = i
GobDecode(asGonBytes, &tmp)
}
}
func Benchmark_GOB_Decode2(b *testing.B) {
tmp := Service{}
for i := 0; i < b.N*10; i++ {
as.Port = i
GobDecode(serviceGonBytes, &tmp)
}
}
執行命令:
go test -bench=".*"
測評結果;
$ go test -bench=".*"
Benchmark_STD_Marshal1-4 50000 31224 ns/op
Benchmark_STD_Marshal2-4 30000 49598 ns/op
Benchmark_EASYJSON_STD_Marshal1-4 30000 45778 ns/op
Benchmark_EASYJSON_STD_Marshal2-4 30000 50440 ns/op
Benchmark_EASYJSON_Marshal1-4 100000 14387 ns/op
Benchmark_EASYJSON_Marshal2-4 100000 16009 ns/op
Benchmark_ITERATOR_Marshal1-4 100000 14899 ns/op
Benchmark_ITERATOR_Marshal2-4 100000 21629 ns/op
Benchmark_FFJSON_Marshal1-4 50000 31633 ns/op
Benchmark_FFJSON_Marshal2-4 30000 51668 ns/op
Benchmark_GOB_Encode1-4 20000 97099 ns/op
Benchmark_GOB_Encode2-4 10000 153158 ns/op
Benchmark_STD_Unmarshal1-4 20000 89211 ns/op
Benchmark_STD_Unmarshal2-4 20000 76442 ns/op
Benchmark_EASYJSON_STD_Unmarshal1-4 30000 57695 ns/op
Benchmark_EASYJSON_STD_Unmarshal2-4 20000 66269 ns/op
Benchmark_EASYJSON_Unmarshal1-4 100000 19028 ns/op
Benchmark_EASYJSON_Unmarshal2-4 100000 22035 ns/op
Benchmark_ITERATOR_UnMarshal1-4 50000 35942 ns/op
Benchmark_ITERATOR_UnMarshal2-4 50000 36462 ns/op
Benchmark_FFJSON_UnMarshal1-4 20000 80290 ns/op
Benchmark_FFJSON_UnMarshal2-4 20000 78431 ns/op
Benchmark_GOB_Decode1-4 3000 377698 ns/op
Benchmark_GOB_Decode2-4 3000 463472 ns/op
PASS
ok studygo/jsonbench 49.174s
結論
- 哪一個類庫最快?
答:是測評類庫中最快的。速度:easyjson => iterator => encoding/json => ffjson - 是否存在坑?
答:easyjson
有一個坑,從程式碼中可以看到Benchmark_EASYJSON_STD_*
的方法,是因為easyjson生成的程式碼中已經包含了MarshalJSON
和UnmarshalJSON
方法,那麼只要對這些結構體執行json.marshalJSON
和json.UnmarshalJSON
都會預設呼叫easyjson生成的方法。本人執行多次,都會發現呼叫easyjson生成的MarshalJSON
方法比標準庫中的慢一些達到50%左右,但是呼叫easyjson
生成的UnmarshalJSON
比標準庫的快一些大概20%。 - 如何選擇?
答:easyjson
速度雖然比較快,但也是存在一些不適合的場景,比如如果需要對interface
介面進行序列化時候。所以建議採用easyjson
與標準庫結合。
相關文章
- Golang 的 JSON 包GolangJSON
- Golang json 解析GolangJSON
- golang json 示例GolangJSON
- 細說 Golang 的 JSON 解析GolangJSON
- 再談JSON/XMLJSONXML
- Golang 流式解析 JsonGolangJSON
- golang json 效能分析GolangJSON
- Golang 中使用 JSON 的小技巧GolangJSON
- golang json處理問題GolangJSON
- GoLang之Concurrency再討論Golang
- 再論 golang 環境配置建議Golang
- golang中的log庫Golang
- Json序列化在golang中的應用JSONGolang
- Golang 網路庫併發吞吐量測試Golang
- golang讀取檔案的json資料流,並解析到struct,儲存到資料庫GolangJSONStruct資料庫
- JSON簡介(java中的json庫使用)JSONJava
- Golang高效能json包:easyjsonGolangJSON
- golang中struct、json、map互相轉化GolangStructJSON
- Golang | IO庫Golang
- Golang 的 Elastic 連結庫GolangAST
- golang通過反射使用json字串呼叫struct的指定方法及返回json結果Golang反射JSON字串Struct
- golang 解析php輸出json相容問題GolangPHPJSON
- golang 版本的 curl 請求庫Golang
- Golang專案的測試實踐Golang
- golang結構體json的時間格式化解決方案Golang結構體JSON
- golang 單元測試Golang
- Golang高效實踐之interface、reflection、json實踐GolangJSON
- Golang:將日誌以Json格式輸出到KafkaGolangJSONKafka
- Golang操作結構體、Map轉化為JSONGolang結構體JSON
- php 透過 JSON RPC 與 golang 通訊PHPJSONRPCGolang
- 再論“庫存控制”
- Golang結構體json的時間格式化解決辦法Golang結構體JSON
- Golang 效能測試 (3) 跟蹤刨析 golang traceGolang
- Json Schema簡介和Json Schema的.net實現庫 LateApexEarlySpeed.Json.SchemaJSON
- 請教Golang處理Json, 處理的是 List of Json這種情況,大家有何高見?GolangJSON
- Golang 編寫測試教程Golang
- golang 表格驅動測試Golang
- python比較json/dictionary的庫PythonJSON