LoginSignup
29
28

More than 5 years have passed since last update.

Go の msgpack ライブラリ比較

Last updated at Posted at 2014-10-09

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
}
29
28
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
28