Go で msgpack を扱うには github.com/ugorji/go/codec を使っているのですが、最近 github.com/vmihailenco/msgpack を見つけてちょっとよさそうだったのでベンチマークを取ってみました。
コード
package main
import (
"bytes"
"os"
"testing"
"time"
"github.com/ugorji/go/codec"
"github.com/vmihailenco/msgpack"
)
type Event struct {
Tag string `codec:"tag" msgpack:"tag"`
Time time.Time `codec:"time" msgpack:"time"`
Record map[string]interface{}
}
var (
mh = &codec.MsgpackHandle{RawToString: true}
event *Event
)
func init() {
event = &Event{
Tag: "sysstat.process",
Time: time.Now(),
Record: map[string]interface{}{
"pid": 12353,
"name": "fluxion",
"cmd": "fluxion -c fluxion.toml",
"rss": 3563520,
"vms": 11005952,
"shared": 2666496,
"cpu_time": 80,
},
}
}
func BenchmarkCodecEncode(b *testing.B) {
buf := &bytes.Buffer{}
enc := codec.NewEncoder(buf, mh)
for i := 0; i < b.N; i++ {
enc.Encode(event)
buf.Reset()
}
}
func BenchmarkCodecDecode(b *testing.B) {
buf := &bytes.Buffer{}
codec.NewEncoder(buf, mh).Encode(event)
r := bytes.NewReader(buf.Bytes())
b.ResetTimer()
var ev Event
dec := codec.NewDecoder(r, mh)
for i := 0; i < b.N; i++ {
dec.Decode(&ev)
r.Seek(0, os.SEEK_SET)
}
}
func BenchmarkMsgpackEncode(b *testing.B) {
buf := &bytes.Buffer{}
enc := msgpack.NewEncoder(buf)
for i := 0; i < b.N; i++ {
enc.Encode(event)
buf.Reset()
}
}
func BenchmarkMsgpackDecode(b *testing.B) {
buf := &bytes.Buffer{}
msgpack.NewEncoder(buf).Encode(event)
r := bytes.NewReader(buf.Bytes())
b.ResetTimer()
var ev Event
dec := msgpack.NewDecoder(r)
for i := 0; i < b.N; i++ {
dec.Decode(&ev)
r.Seek(0, os.SEEK_SET)
}
}
実行結果
$ go test -bench .
testing: warning: no tests to run
PASS
BenchmarkCodecEncode 1000000 1970 ns/op
BenchmarkCodecDecode 1000000 2543 ns/op
BenchmarkMsgpackEncode 500000 5274 ns/op
BenchmarkMsgpackDecode 500000 6793 ns/op
ok _/Users/yoshihisa/junk/msgpack 10.761s
github.com/ugorji/go/codec の方がだいぶ速いですね。
まとめ
- github.com/ugorji/go/codec
- 速い
- ext type を使わずに Go の type と binary を変換する仕組みがある ※追記参照
- ext type 対応
- omitempty 対応
- anonymous field のインライン化対応
- github.com/vmihailenco/msgpack
- コードが読みやすい
- ext type を使わずに Go の type と binary を変換する仕組みがある
- ext type 非対応
- omitempty 非対応
- anonymous field のインライン化非対応
基本的には github.com/ugorji/go/codec を使うのが良さそうです。
~~github.com/vmihailenco/msgpack の方は、例えば time.Time を int64 型で表現したいけど ext type を定義したくない時とかに使えそうですね。~~ただし decode する時に型のガイドが必要になるので、interface{} 型には decode できなくなってしまいますが。
追記
ext type を使わずに Go の type と binary を相互変換する仕組みは github.com/ugorji/go/codec には無いと思っていましたが、よく見たらありました。以下のインタフェースを実装しておけばいいようです。
type binaryUnmarshaler interface {
UnmarshalBinary(data []byte) error
}
type binaryMarshaler interface {
MarshalBinary() (data []byte, err error)
}
time.Time はこのインタフェースを実装しているので(それに合わせて定義した?)何もしなくても Go 同士だとよしなに扱えるようです。
なお、github.com/vmihailenco/msgpack の場合は以下のインタフェースを実装します。
type encoder interface {
EncodeMsgpack(io.Writer) error
}
type decoder interface {
DecodeMsgpack(io.Reader) error
}