Go
golang
msgpack

Go の msgpack ライブラリ比較

More than 3 years have passed since last update.

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
}