寫項目一直需要進行序列化,聽到了,也看到了很多同學老師對各個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 jsonbenchimport ( "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_kaleidoscope.dev.igetget.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, } 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_kaleidoscope.dev.igetget.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/opBenchmark_STD_Marshal2-4 30000 49598 ns/opBenchmark_EASYJSON_STD_Marshal1-4 30000 45778 ns/opBenchmark_EASYJSON_STD_Marshal2-4 30000 50440 ns/opBenchmark_EASYJSON_Marshal1-4 100000 14387 ns/opBenchmark_EASYJSON_Marshal2-4 100000 16009 ns/opBenchmark_ITERATOR_Marshal1-4 100000 14899 ns/opBenchmark_ITERATOR_Marshal2-4 100000 21629 ns/opBenchmark_FFJSON_Marshal1-4 50000 31633 ns/opBenchmark_FFJSON_Marshal2-4 30000 51668 ns/opBenchmark_GOB_Encode1-4 20000 97099 ns/opBenchmark_GOB_Encode2-4 10000 153158 ns/opBenchmark_STD_Unmarshal1-4 20000 89211 ns/opBenchmark_STD_Unmarshal2-4 20000 76442 ns/opBenchmark_EASYJSON_STD_Unmarshal1-4 30000 57695 ns/opBenchmark_EASYJSON_STD_Unmarshal2-4 20000 66269 ns/opBenchmark_EASYJSON_Unmarshal1-4 100000 19028 ns/opBenchmark_EASYJSON_Unmarshal2-4 100000 22035 ns/opBenchmark_ITERATOR_UnMarshal1-4 50000 35942 ns/opBenchmark_ITERATOR_UnMarshal2-4 50000 36462 ns/opBenchmark_FFJSON_UnMarshal1-4 20000 80290 ns/opBenchmark_FFJSON_UnMarshal2-4 20000 78431 ns/opBenchmark_GOB_Decode1-4 3000 377698 ns/opBenchmark_GOB_Decode2-4 3000 463472 ns/opPASSok 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
與標準庫結合。