几种Go序列化库的性能比较

序列化库在网络传输,RPC,数据库访问等环境中经常用到,它的性能的好坏直接影响着整个产品的性能。
本文列出了几种高性能的Go语言的序列化库,并通过一个简单的无循环引用的数据结构测试它们的性能。
测试代码: gosercomp at github

03/14/2016 更新。 增加Thrift/Avro/Gencode的性能比较。

测试的 Serializers

以golang自带的encoding/jsonencoding/xml为基准,测试以下性能比较好的几种序列化库。

排除的 Serializers

基于 alecthomas 已有的测试,下面的库由于性能的原因没有进行测试。

测试环境

add ./bin into the PATH environment variable.

  • 对于github.com/youtube/vitess/go/bson,你可能需要安装 goimportscodegen:
1
2
3
4
go get github.com/youtube/vitess/go/bson
go get golang.org/x/tools/cmd/goimports
go get github.com/youtube/vitess/tree/master/go/cmd/bsongen
bsongen -file data.go -o bson_data.go -type ColorGroup
  • 对于 MessagePack,你需要安装库以及利用go generate生成相关的类:
1
2
go get github.com/tinylib/msgp
go generate
  • 对于ProtoBuf,你需要安装protoc编译器,以及protoc库以及生成相关的类:
1
2
3
go get github.com/golang/protobuf/proto
go get github.com/golang/protobuf/protoc-gen-go
go generate
  • 对于gogo/protobuf,你需要安装库以及生成相关的类:
1
2
3
go get github.com/gogo/protobuf/gogoproto
go get github.com/gogo/protobuf/protoc-gen-gofast
go generate
  • 对于flatbuffers,你需要安装thrift编译器, 以及flatbuffers库:
1
2
go get github.com/google/flatbuffers/go
go generate
1
2
go get git.apache.org/thrift.git/lib/go/thrift
go generate
  • 对于Avro,你需要安装goavro库:
1
2
go get github.com/linkedin/goavro
go generate
  • 对于gencode,你需要安装gencode库,并使用gencode库的工具产生数据对象:
1
2
go get github.com/andyleap/gencode
bin\gencode.exe go -schema=gencode.schema -package gosercomp

事实上,这里通过go generate生成相关的类,你也可以通过命令行生成,请参考data.go中的注释。 但是你需要安装相关的工具,如Thrift,并把它们加入到环境变量Path中

运行下面的命令测试:

1
go test -bench=. -benchmem

测试数据

所有的测试基于以下的struct,自动生成的struct, 比如protobuf也和此结构基本一致。

1
2
3
4
5
type ColorGroup struct {
ID int `json:"id" xml:"id,attr""`
Name string `json:"name" xml:"name"`
Colors []string `json:"colors" xml:"colors"`
}

性能测试结果

以下性能的测试结果是在我的T430u/Windows 10上的测试结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
benchmark _name iter time/iter alloc bytes/iter allocs/iter
-------------------------------------------------------------------------------------------------------------------------
BenchmarkMarshalByJson-4 1000000 1795 ns/op 376 B/op 4 allocs/op
BenchmarkUnmarshalByJson-4 500000 3927 ns/op 296 B/op 9 allocs/op
BenchmarkMarshalByXml-4 200000 8216 ns/op 4801 B/op 12 allocs/op
BenchmarkUnmarshalByXml-4 50000 26284 ns/op 2807 B/op 67 allocs/op
BenchmarkMarshalByBson-4 500000 3258 ns/op 1248 B/op 14 allocs/op
BenchmarkUnmarshalByBson-4 1000000 1433 ns/op 272 B/op 7 allocs/op
BenchmarkMarshalByMsgp-4 5000000 259 ns/op 80 B/op 1 allocs/op
BenchmarkUnmarshalByMsgp-4 3000000 466 ns/op 32 B/op 5 allocs/op
BenchmarkMarshalByProtoBuf-4 2000000 955 ns/op 328 B/op 5 allocs/op
BenchmarkUnmarshalByProtoBuf-4 1000000 1571 ns/op 400 B/op 11 allocs/op
BenchmarkMarshalByGogoProtoBuf-4 10000000 224 ns/op 48 B/op 1 allocs/op
BenchmarkUnmarshalByGogoProtoBuf-4 2000000 828 ns/op 144 B/op 8 allocs/op
BenchmarkMarshalByFlatBuffers-4 3000000 626 ns/op 16 B/op 1 allocs/op
BenchmarkUnmarshalByFlatBuffers-4 100000000 10.4 ns/op 0 B/op 0 allocs/op
BenchmarkUnmarshalByFlatBuffers_withFields-4 3000000 493 ns/op 32 B/op 5 allocs/op
BenchmarkMarshalByThrift-4 2000000 840 ns/op 64 B/op 1 allocs/op
BenchmarkUnmarshalByThrift-4 1000000 1575 ns/op 96 B/op 6 allocs/op
BenchmarkMarshalByAvro-4 1000000 1330 ns/op 133 B/op 7 allocs/op
BenchmarkUnmarshalByAvro-4 200000 7036 ns/op 1680 B/op 63 allocs/op
BenchmarkMarshalByGencode-4 20000000 66.2 ns/op 0 B/op 0 allocs/op
BenchmarkUnmarshalByGencode-4 5000000 258 ns/op 32 B/op 5 allocs/op

多次测试结果差不多。 从结果上上来看, MessagePack,gogo/protobuf,和flatbuffers差不多,这三个优秀的库在序列化和反序列化上各有千秋,而且都是跨语言的。 从便利性上来讲,你可以选择MessagePackgogo/protobuf都可以,两者都有大厂在用。 flatbuffers有点反人类,因为它的操作很底层,而且从结果上来看,序列化的性能要差一点。但是它有一个好处,那就是如果你只需要特定的字段, 你无须将所有的字段都反序列化。从结果上看,不反序列化字段每个调用只用了9.54纳秒,这是因为字段只有在被访问的时候才从byte数组转化为相应的类型。 因此在特殊的场景下,它可以提高N被的性能。但是序列化的代码的面相太难看了。

新增加了gencode的测试,它表现相当出色,而且生成的字节也非常的小。

官方的Protobuf是通过反射(reflect)实现序列化反序列化的,所以效率不是很高。而gogo/protobuf则是在代码生成的时候提供了Marshal/UnMarshal的方法,它不采用反射的方式,所以性能上比官方的库要好很多。