28
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Go の msgpack ライブラリ比較

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
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
28
Help us understand the problem. What are the problem?