1. Qiita
  2. 投稿
  3. Go

Go の msgpack ライブラリ比較

  • 28
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

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
}