Performance Entity Serialization:BSON vs MessagePack(vs JSON)


137

最近、私はMessagePackを見つけました。これは、GoogleのプロトコルバッファーJSONの代替バイナリシリアル化形式で、どちらも優れています。

また、MongoDBがデータの保存に使用するBSONシリアル化形式もあります。

誰かがBSONとMessagePack違いと欠点を詳しく説明できますか?


パフォーマンスの高いバイナリシリアル化形式のリストを完成させるためだけに、Googleのプロトコルバッファの後継となるGobs もあります。ただし、言及されている他のすべての形式とは対照的に、これらは言語に依存せず、Goの組み込みリフレクション依存しています。少なくともGo以外の言語用のGobsライブラリもあります。


3
主にマーケティングの誇大広告のようです。["compiled"]シリアライゼーションフォーマットのパフォーマンスは、使用されている実装によるものです。一部の形式では本質的にオーバーヘッドが大きくなりますが(たとえば、JSONはすべて動的に処理されるため)、形式自体には「速度がありません」。次に、ページは、それ自体を比較する方法を「選択して選択」します...非常に偏りのない方法です。私のお茶ではありません。

6
訂正:ゴブはプロトコルバッファーを置き換えることを目的としておらず、おそらく置き換えられません。また、ゴブは言語に依存しません(どの言語でも読み書きできます。code.google.com/ p / libgobを参照してください)。Gobは、Goがデータを処理する方法と厳密に一致するように定義されているため、Goで最適に機能します。
カイルC

6
msgpackパフォーマンスベンチマークへのリンクが壊れています(msgpack.org/index/speedtest.png)。
Aliaksei Ramanau 2012年

回答:


197

//私はMessagePackの作者です。この答えは偏っているかもしれません。

フォーマット設計

  1. JSONとの互換性

    その名前にもかかわらず、JSONとのBSONの互換性はMessagePackと比較してそれほど良くありません。

    BSONには、「ObjectId」、「Min key」、「UUID」、「MD5」などの特殊なタイプがあります(これらのタイプはMongoDBで必要とされていると思います)。これらのタイプはJSONと互換性がありません。つまり、オブジェクトをBSONからJSONに変換すると、一部の型情報が失われる可能性がありますが、もちろんこれらの特殊な型がBSONソースにある場合に限られます。1つのサービスでJSONとBSONの両方を使用することは不利な場合があります。

    MessagePackは、JSONとの間で透過的に変換されるように設計されています。

  2. MessagePackはBSONよりも小さい

    MessagePackの形式はBSONよりも簡潔です。その結果、MessagePackはBSONよりも小さいオブジェクトをシリアル化できます。

    たとえば、シンプルなマップ{"a":1、 "b":2}はMessagePackで7バイトにシリアル化されますが、BSONは19バイトを使用します。

  3. BSONはインプレース更新をサポートします

    BSONを使用すると、オブジェクト全体を再シリアル化せずに、格納されたオブジェクトの一部を変更できます。マップ{"a":1、 "b":2}がファイルに格納されていて、 "a"の値を1から2000に更新するとします。

    MessagePackでは、1は1バイトのみを使用しますが、2000は3バイトを使用します。したがって、「b」は変更されませんが、「b」は2バイト後方に移動する必要があります。

    BSONでは、1と2000の両方が5バイトを使用します。この冗長性のため、 "b"を移動する必要はありません。

  4. MessagePackにはRPCがあります

    MessagePack、プロトコルバッファ、Thrift、AvroはRPCをサポートしています。しかし、BSONはそうではありません。

これらの違いは、BSONがストレージ用に設計されているのに対して、MessagePackはもともとネットワーク通信用に設計されていることを意味しています。

実装とAPI設計

  1. MessagePackには型チェックAPI(Java、C ++およびD)があります

    MessagePackは静的型付けをサポートしています。

    JSONまたはBSONで使用される動的型付けは、Ruby、Python、JavaScriptなどの動的言語に役立ちます。しかし、静的言語では面倒です。退屈な型チェックコードを記述する必要があります。

    MessagePackは型チェックAPIを提供します。動的に型付けされたオブジェクトを静的に型付けされたオブジェクトに変換します。以下は簡単な例です(C ++):

    #include <msgpack.hpp>

    class myclass {
    private:
        std::string str;
        std::vector<int> vec;
    public:
        // This macro enables this class to be serialized/deserialized
        MSGPACK_DEFINE(str, vec);
    };

    int main(void) {
        // serialize
        myclass m1 = ...;

        msgpack::sbuffer buffer;
        msgpack::pack(&buffer, m1);

        // deserialize
        msgpack::unpacked result;
        msgpack::unpack(&result, buffer.data(), buffer.size());

        // you get dynamically-typed object
        msgpack::object obj = result.get();

        // convert it to statically-typed object
        myclass m2 = obj.as<myclass>();
    }
  1. MessagePackにはIDLがあります

    型チェックAPIに関連しており、MessagePackはIDLをサポートしています。(仕様は次から入手できます:http : //wiki.msgpack.org/display/MSGPACK/Design+of+IDL

    プロトコルバッファーとスリフトはIDLを必要とし(ダイナミックタイピングをサポートしません)、より成熟したIDL実装を提供します。

  2. MessagePackにはストリーミングAPI(Ruby、Python、Java、C ++など)があります。

    MessagePackはデシリアライザのストリーミングをサポートしています。この機能は、ネットワーク通信に役立ちます。次に例を示します(Ruby)。

    require 'msgpack'

    # write objects to stdout
    $stdout.write [1,2,3].to_msgpack
    $stdout.write [1,2,3].to_msgpack

    # read objects from stdin using streaming deserializer
    unpacker = MessagePack::Unpacker.new($stdin)
    # use iterator
    unpacker.each {|obj|
      p obj
    }

33
MessagePackは、Google Protobufとデータサイズの点でどのように比較されますか?
Ellis

4
最初の点は、MessagePackがJSONで表現できないrawバイト機能を備えているという事実に触れています。したがって、その点ではBSONと同じです...

4
@lttlrck通常、生のバイトは、チャネルの両側で予期および合意されていない限り、文字列(通常はutf-8)と見なされます。msgpackは、ストリーム/シリアライゼーションフォーマットとして使用されます。また、jsonの冗長性は低くなりますが、人間が読むこともできません。
Tracker1

4
「MessagePackには型チェックAPIがあります。BSONにはありません。」完全に正確ではありません。これは、静的に型付けされた言語でのBSON実装にも実際に当てはまります。
Brandon Black

1
MessagePackにBINARYデータ型が含まれるようになったため、JSONに対する1-1逆シリアル化の互換性の引数は完全に正しくなくなりました。
zimbatm

16

この質問は現時点では少し古いと思います...クライアント/サーバー環境がどのように見えるかに依存することを述べることは非常に重要だと思います。

メッセージキューシステムやディスクへのストリーミングログエントリなど、検査せずに複数回バイトを渡す場合は、コンパクトサイズを強調するためにバイナリエンコーディングを使用することをお勧めします。そうでなければ、それは異なる環境でのケースバイケースの問題です。

一部の環境では、msgpack / protobufとの高速なシリアル化と非シリアル化が可能ですが、その他の環境ではそれほど多くありません。一般に、言語/環境のレベルが低いほど、バイナリシリアライゼーションが機能します。より高いレベルの言語(node.js、.Net、JVM)では、JSONのシリアル化が実際に高速であることがよくあります。次に問題は、ネットワークのオーバーヘッドがメモリ/ CPUよりも制限されているかどうかです。

msgpack対bson対プロトコルバッファについて... msgpackはグループの最小バイトであり、プロトコルバッファはほぼ同じです。BSONは他の2つよりも幅広いネイティブタイプを定義しているため、オブジェクトモードに一致する可能性がありますが、これにより冗長になります。プロトコルバッファには、ストリーミングするように設計されているという利点があります。これにより、バイナリ転送/ストレージフォーマットとしてより自然なフォーマットになります。

個人的には、軽いトラフィックが必要でない限り、JSONが直接提供する透明性を重視します。gzip圧縮されたデータを使用するHTTP経由では、ネットワークオーバーヘッドの違いは、フォーマット間の問題がさらに少なくなります。


6
ネイティブMsgPackがあるのみ(常に存在しているテキスト)キーの長さであるようにProtocolBuffersサイズ単位との効率的な短い -例えば「a」または「B」として、またはそうでなければ全体ペイロードの微々たる部分です。それらは、IDL /コンパイルを使用してフィールド記述子をIDにマップするProtocolBuffersでは常に不足しています。これは、MsgPackを「動的」にするものでもあり、ProtocolBuffersはおそらく間違いなく..
user2864740

2
GZIP /デフレートがされていますけれども、エンドポイントは良いですが、本当に良い、このようなキーは(上など、MsgPack、JSON / BSON、およびXML「長いが、たくさん繰り返し」である場合には、キーの冗長性を処理している多くの記録)が、助けにはなりませんここでは、ProtocolBuffersを使用しています。Avroは、スキーマを個別に送信することにより、キーの冗長性を手動で削除します。
user2864740 2015

4

クイックテストは、縮小されたJSONがバイナリMessagePackよりも高速にデシリアライズされることを示しています。テストでは、Article.jsonは550kbの縮小JSON、Article.mpackは420kbのMPバージョンです。もちろん実装の問題かもしれません。

メッセージパック:

//test_mp.js
var msg = require('msgpack');
var fs = require('fs');

var article = fs.readFileSync('Article.mpack');

for (var i = 0; i < 10000; i++) {
    msg.unpack(article);    
}

JSON:

// test_json.js
var msg = require('msgpack');
var fs = require('fs');

var article = fs.readFileSync('Article.json', 'utf-8');

for (var i = 0; i < 10000; i++) {
    JSON.parse(article);
}

時間は次のとおりです。

Anarki:Downloads oleksii$ time node test_mp.js 

real    2m45.042s
user    2m44.662s
sys     0m2.034s

Anarki:Downloads oleksii$ time node test_json.js 

real    2m15.497s
user    2m15.458s
sys     0m0.824s

スペースは節約されますが、高速ですか?番号。

テスト済みバージョン:

Anarki:Downloads oleksii$ node --version
v0.8.12
Anarki:Downloads oleksii$ npm list msgpack
/Users/oleksii
└── msgpack@0.1.7  

7
確かに実装に依存します。Python 2.7.3を使用した私のテストでは、489Kのtest.json(409K相当のtest.msgpack)をアンパックし、10,000回の反復でsimplejson2.6.2は66.7秒msgpackかかり、0.2.2は28.8 秒しかかからないことを示しています。

2
このArticle.jsonはどこから来たのですか?
Ant6n 2014

皆さん、上記の私のコメントにテストコードがあります。他に何を期待していましたか?Article.jsonは、プロジェクトのjsonシリアル化オブジェクトです。そして今のところ、それらの結果はとにかく無関係である可能性があります
Oleksiy Khilkevich 14

14
JSはJSONをC ++でネイティブに実装し、msgpackはJSで実装するため、これは公平なパフォーマンス比較ではありません。
Alex Panchenko

2
あなたはMessagePackをローマ人よりラテン語で話せるようにしようとしています。JSONはJavaScriptのネイティブ(C ++)ですが、MessagePackはJavaScriptで記述され、解釈されます。これは基本的に2つのコードスニペットを比較しています。1つはJavaScriptで記述され、もう1つはC ++で記述されています。
ラマザンポ

0

まだ言及されていない主な違いは、BSONにはドキュメント全体とさらにネストされたサブドキュメントのサイズ情報がバイト単位で含まれていることです。

document    ::=     int32 e_list

これには、サイズとパフォーマンスが重要な制限された環境(組み込みなど)に対して2つの大きな利点があります。

  1. 解析するデータが完全なドキュメントを表しているかどうか、またはある時点で(接続またはストレージから)要求する必要があるかどうかをすぐに確認できます。これは非同期操作である可能性が高いため、解析前に新しいリクエストを送信している可能性があります。
  2. データには、無関係な情報を含むサブドキュメント全体が含まれている場合があります。BSONを使用すると、サブドキュメントのサイズ情報を使用してスキップすることにより、サブドキュメントを通過した次のオブジェクトに簡単に移動できます。一方、msgpackには、マップと呼ばれるもの(BSONのサブドキュメントと同様)内の要素の数が含まれています。これは間違いなく有用な情報ですが、パーサーには役立ちません。それでもマップ内のすべてのオブジェクトを解析する必要があり、単にスキップすることはできません。データの構造によっては、これがパフォーマンスに大きな影響を与える可能性があります。

0

MessagePackとBSONのエンコードとデコードの速度を比較するためのクイックベンチマークを作成しました。大きなバイナリ配列がある場合、BSONは少なくとも高速です。

BSON writer: 2296 ms (243487 bytes)
BSON reader: 435 ms
MESSAGEPACK writer: 5472 ms (243510 bytes)
MESSAGEPACK reader: 1364 ms

C#Newtonsoft.JsonとMessagePackをneueccで使用する:

    public class TestData
    {
        public byte[] buffer;
        public bool foobar;
        public int x, y, w, h;
    }

    static void Main(string[] args)
    {
        try
        {
            int loop = 10000;

            var buffer = new TestData();
            TestData data2;
            byte[] data = null;
            int val = 0, val2 = 0, val3 = 0;

            buffer.buffer = new byte[243432];

            var sw = new Stopwatch();

            sw.Start();
            for (int i = 0; i < loop; i++)
            {
                data = SerializeBson(buffer);
                val2 = data.Length;
            }

            var rc1 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data2 = DeserializeBson(data);
                val += data2.buffer[0];
            }
            var rc2 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data = SerializeMP(buffer);
                val3 = data.Length;
                val += data[0];
            }

            var rc3 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data2 = DeserializeMP(data);
                val += data2.buffer[0];
            }
            var rc4 = sw.ElapsedMilliseconds;

            Console.WriteLine("Results:", val);
            Console.WriteLine("BSON writer: {0} ms ({1} bytes)", rc1, val2);
            Console.WriteLine("BSON reader: {0} ms", rc2);
            Console.WriteLine("MESSAGEPACK writer: {0} ms ({1} bytes)", rc3, val3);
            Console.WriteLine("MESSAGEPACK reader: {0} ms", rc4);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }

        Console.ReadLine();
    }

    static private byte[] SerializeBson(TestData data)
    {
        var ms = new MemoryStream();

        using (var writer = new Newtonsoft.Json.Bson.BsonWriter(ms))
        {
            var s = new Newtonsoft.Json.JsonSerializer();
            s.Serialize(writer, data);
            return ms.ToArray();
        }
    }

    static private TestData DeserializeBson(byte[] data)
    {
        var ms = new MemoryStream(data);

        using (var reader = new Newtonsoft.Json.Bson.BsonReader(ms))
        {
            var s = new Newtonsoft.Json.JsonSerializer();
            return s.Deserialize<TestData>(reader);
        }
    }

    static private byte[] SerializeMP(TestData data)
    {
        return MessagePackSerializer.Typeless.Serialize(data);
    }

    static private TestData DeserializeMP(byte[] data)
    {
        return (TestData)MessagePackSerializer.Typeless.Deserialize(data);
    }

0

さて、作者が言ったように、BSONがストレージ用に設計されている間、MessagePackはもともとネットワーク通信用に設計されています。

MessagePackはBSONが冗長である一方でコンパクトです。BSONはCURD(時間効率)向けに設計されていますが、MessagePackはスペース効率を高めることを目的としています。

最も重要なのは、MessagePackの型システム(プレフィックス)がハフマンエンコーディングに従っていることです。ここでは、MessagePackのハフマンツリーを描画しました(画像を表示するにはリンクをクリックしてください):

MessagePackのハフマンツリー

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.