Serialisasi Entitas Performant: BSON vs MessagePack (vs JSON)

137

Baru-baru ini saya menemukan MessagePack , format serialisasi biner alternatif untuk Protocol Buffer dan JSON Google yang juga mengungguli keduanya.

Juga ada format serialisasi BSON yang digunakan oleh MongoDB untuk menyimpan data.

Adakah yang bisa menjelaskan perbedaan dan keunggulan BSON vs MessagePack ?


Sekadar melengkapi daftar format serialisasi biner yang berkinerja baik: Ada juga Gobs yang akan menjadi penerus Protocol Buffer Google . Namun berbeda dengan semua format lain yang disebutkan, yang bukan bahasa-agnostik dan mengandalkan refleksi bawaan Go, ada juga pustaka Gobs untuk setidaknya bahasa lain selain Go.

Alex
sumber
3
Sepertinya sebagian besar seperti beban hype pemasaran. Kinerja format serialisasi ["terkompilasi"] disebabkan oleh implementasi yang digunakan. Sementara beberapa format memiliki lebih banyak overhead (misalnya JSON karena semuanya diproses secara dinamis), format itu sendiri tidak "memiliki kecepatan". Halaman ini kemudian melanjutkan ke "memilih dan memilih" bagaimana ia membandingkan dirinya sendiri ... itu cara yang sangat tidak bias. Bukan cangkir teh saya.
6
Koreksi: Gobs tidak dimaksudkan untuk menggantikan Protocol Buffer, dan mungkin tidak akan pernah. Selain itu, Gobs adalah bahasa agnostik (dapat dibaca / ditulis dalam bahasa apa pun, lihat code.google.com/p/libgob ), tetapi Gobs didefinisikan untuk sangat cocok dengan bagaimana Go menangani data, sehingga bekerja paling baik dengan Go.
Kyle C
6
Tautan ke tolok ukur kinerja msgpack rusak ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau

Jawaban:

199

// Harap dicatat bahwa saya adalah penulis MessagePack. Jawaban ini mungkin bias.

Desain format

  1. Kompatibilitas dengan JSON

    Terlepas dari namanya, kompatibilitas BSON dengan JSON tidak begitu baik dibandingkan dengan MessagePack.

    BSON memiliki tipe khusus seperti "ObjectId", "Min key", "UUID" atau "MD5" (menurut saya tipe ini dibutuhkan oleh MongoDB). Jenis ini tidak kompatibel dengan JSON. Itu berarti beberapa jenis informasi dapat hilang saat Anda mengonversi objek dari BSON ke JSON, tetapi tentu saja hanya jika jenis khusus ini ada di sumber BSON. Ini bisa menjadi kerugian untuk menggunakan JSON dan BSON dalam satu layanan.

    MessagePack dirancang untuk dikonversi secara transparan dari / ke JSON.

  2. MessagePack lebih kecil dari BSON

    Format MessagePack kurang bertele-tele dari BSON. Hasilnya, MessagePack dapat membuat serial objek yang lebih kecil dari BSON.

    Misalnya, peta sederhana {"a": 1, "b": 2} diserialkan dalam 7 byte dengan MessagePack, sedangkan BSON menggunakan 19 byte.

  3. BSON mendukung pembaruan di tempat

    Dengan BSON, Anda dapat memodifikasi bagian dari objek yang disimpan tanpa menserialisasi ulang seluruh objek. Misalkan peta {"a": 1, "b": 2} disimpan dalam sebuah file dan Anda ingin memperbarui nilai "a" dari 1 menjadi 2000.

    Dengan MessagePack, 1 hanya menggunakan 1 byte tetapi 2000 menggunakan 3 byte. Jadi "b" harus dipindahkan mundur sebanyak 2 byte, sedangkan "b" tidak diubah.

    Dengan BSON, 1 dan 2000 menggunakan 5 byte. Karena verbositas ini, Anda tidak perlu memindahkan "b".

  4. MessagePack memiliki RPC

    MessagePack, Protocol Buffer, Thrift dan Avro mendukung RPC. Tapi BSON tidak.

Perbedaan ini menyiratkan bahwa MessagePack pada awalnya dirancang untuk komunikasi jaringan sedangkan BSON dirancang untuk penyimpanan.

Implementasi dan desain API

  1. MessagePack memiliki API pemeriksaan tipe (Java, C ++ dan D)

    MessagePack mendukung pengetikan statis.

    Pengetikan dinamis yang digunakan dengan JSON atau BSON berguna untuk bahasa dinamis seperti Ruby, Python, atau JavaScript. Tapi merepotkan untuk bahasa statis. Anda harus menulis kode pengecekan tipe yang membosankan.

    MessagePack menyediakan API pemeriksaan tipe. Ini mengubah objek yang diketik secara dinamis menjadi objek yang diketik secara statis. Berikut adalah contoh sederhana (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 memiliki IDL

    Ini terkait dengan API pemeriksaan tipe, MessagePack mendukung IDL. (spesifikasi tersedia dari: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Protocol Buffer dan Thrift memerlukan IDL (tidak mendukung pengetikan dinamis) dan menyediakan implementasi IDL yang lebih matang.

  2. MessagePack memiliki API streaming (Ruby, Python, Java, C ++, ...)

    MessagePack mendukung streaming deserializers. Fitur ini berguna untuk komunikasi jaringan. Berikut adalah contohnya (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
    }
Sadayuki Furuhashi
sumber
33
Bagaimana MessagePack dibandingkan dengan Google Protobufs dalam hal ukuran data, dan akibatnya, kinerja udara?
Ellis
4
Poin pertama menjelaskan fakta bahwa MessagePack memiliki kapabilitas byte mentah yang tidak dapat direpresentasikan di JSON. Jadi sama saja dengan BSON dalam hal itu ...
4
@lttlrck Umumnya, byte mentah diasumsikan sebagai string (biasanya utf-8), kecuali diharapkan dan disetujui di kedua sisi saluran. msgpack digunakan sebagai format streaming / serialisasi ... dan tidak terlalu bertele-tele seperti json .. meskipun juga kurang dapat dibaca manusia.
Tracker1
4
"MessagePack memiliki API pemeriksaan tipe. BSON Tidak." Tidak sepenuhnya akurat. Ini sebenarnya berlaku untuk implementasi BSON dalam bahasa yang diketik secara statis juga.
Brandon Black
1
MessagePack sekarang memiliki tipe data BINARY sehingga argumen kompatibilitas de-serialisasi 1-1 ke JSON tidak sepenuhnya benar lagi.
zimbatm
16

Saya tahu bahwa pertanyaan ini agak ketinggalan zaman pada saat ini ... Saya pikir sangat penting untuk menyebutkan bahwa itu tergantung pada seperti apa lingkungan klien / server Anda.

Jika Anda meneruskan byte beberapa kali tanpa pemeriksaan, seperti dengan sistem antrian pesan atau entri log streaming ke disk, Anda mungkin lebih memilih pengkodean biner untuk menekankan ukuran kompak. Jika tidak, itu adalah masalah kasus per kasus dengan lingkungan yang berbeda.

Beberapa lingkungan dapat memiliki serialisasi dan deserialisasi yang sangat cepat ke / dari msgpack / protobuf, yang lain tidak begitu banyak. Secara umum, semakin rendah level bahasa / lingkungan, serialisasi biner yang lebih baik akan berfungsi. Dalam bahasa tingkat yang lebih tinggi (node.js, .Net, JVM) Anda akan sering melihat bahwa serialisasi JSON sebenarnya lebih cepat. Pertanyaannya kemudian menjadi apakah overhead jaringan Anda lebih atau kurang dibatasi daripada memori / cpu Anda?

Berkenaan dengan msgpack vs bson vs buffer protokol ... msgpack adalah byte terkecil dari grup, buffer protokol hampir sama. BSON mendefinisikan tipe asli yang lebih luas daripada dua lainnya, dan mungkin lebih cocok untuk mode objek Anda, tetapi ini membuatnya lebih bertele-tele. Buffer protokol memiliki keuntungan karena dirancang untuk streaming ... yang membuatnya menjadi format yang lebih alami untuk format transfer / penyimpanan biner.

Secara pribadi, saya akan bersandar pada transparansi yang ditawarkan JSON secara langsung, kecuali jika ada kebutuhan yang jelas untuk lalu lintas yang lebih ringan. Melalui HTTP dengan data yang di-gzip, perbedaan dalam overhead jaringan bahkan lebih sedikit menjadi masalah antar format.

Pelacak 1
sumber
6
Native MsgPack hanya efisien dengan ukuran ProtocolBuffers karena panjang kunci (yang selalu ada teks) pendek seperti "a" atau "b" - atau merupakan bagian yang tidak signifikan dari keseluruhan payload . Mereka selalu pendek di ProtocolBuffers yang menggunakan IDL / kompilasi untuk memetakan deskriptor bidang ke id. Ini juga yang membuat MsgPack "dinamis", yang tentunya bukan ProtocolBuffers ..
user2864740
2
Titik akhirnya bagus: gzip / deflate benar - benar bagus menangani redundansi kunci dalam kasus di mana kunci tersebut "lebih panjang tetapi banyak berulang" (MsgPack, JSON / BSON, dan XML, dll pada banyak catatan) tetapi tidak membantu ProtocolBuffers sama sekali di sini .. Avro melakukan penghapusan redundansi kunci secara manual dengan mengirimkan skema secara terpisah.
pengguna2864740
4

Pengujian cepat menunjukkan JSON yang diperkecil dideserialisasi lebih cepat daripada biner MessagePack. Dalam pengujian, Article.json adalah 550kb JSON yang diperkecil, Article.mpack adalah versi 420kb MP-nya. Mungkin masalah implementasi tentu saja.

MessagePack:

//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);
}

Jadi waktunya adalah:

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

Jadi ruang disimpan, tetapi lebih cepat? Tidak.

Versi yang diuji:

Anarki:Downloads oleksii$ node --version
v0.8.12
Anarki:Downloads oleksii$ npm list msgpack
/Users/oleksii
└── [email protected]  
Oleksiy Khilkevich
sumber
7
Sangat bergantung pada implementasinya. Pengujian saya dengan Python 2.7.3 membongkar test.json 489K (setara dengan 409K test.msgpack) menunjukkan bahwa untuk 10.000 iterasi simplejson2.6.2 membutuhkan waktu 66,7 detik dan msgpack0,2.2 hanya membutuhkan 28,8.
Hari
2
Dari mana asal Article.json ini?
Ant6n
Teman-teman, kode uji ada di komentar saya di atas, apa lagi yang Anda harapkan, Article.json adalah objek serialisasi json dari proyek kami. Dan sekarang hasil itu mungkin tidak relevan lagi
Oleksiy Khilkevich
14
Ini bukan perbandingan performa yang adil, karena JS telah menerapkan JSON secara native di C ++, sedangkan msgpack di JS.
Alex Panchenko
2
Anda mencoba membuat MessagePack berbicara bahasa Latin lebih baik daripada bahasa Romawi. JSON adalah native (C ++) untuk JavaScript sementara MessagePack ditulis dalam JavaScript, yang diinterpretasikan. Ini pada dasarnya membandingkan dua cuplikan kode, satu ditulis dalam JavaScript dan lainnya ditulis dalam C ++.
Ramazan Polat
0

Perbedaan utama yang belum disebutkan adalah bahwa BSON berisi informasi ukuran dalam byte untuk seluruh dokumen dan sub-dokumen bersarang lebih lanjut.

document    ::=     int32 e_list

Ini memiliki dua manfaat utama untuk lingkungan terbatas (misalnya tertanam) di mana ukuran dan kinerja penting.

  1. Anda dapat segera memeriksa apakah data yang akan Anda parse mewakili dokumen lengkap atau jika Anda akan perlu meminta lebih banyak di beberapa titik (baik itu dari beberapa koneksi atau penyimpanan). Karena ini kemungkinan besar merupakan operasi asinkron, Anda mungkin sudah mengirim permintaan baru sebelum parsing.
  2. Data Anda mungkin berisi seluruh sub-dokumen dengan informasi yang tidak relevan untuk Anda. BSON memungkinkan Anda untuk dengan mudah melintasi ke objek berikutnya melewati sub-dokumen dengan menggunakan informasi ukuran dari sub-dokumen untuk melewatinya. msgpack di sisi lain berisi jumlah elemen di dalam apa yang disebut peta (mirip dengan sub-dokumen BSON). Meskipun ini adalah informasi yang tidak diragukan lagi berguna, ini tidak membantu pengurai. Anda masih harus mengurai setiap objek di dalam peta dan tidak bisa melewatkannya begitu saja. Bergantung pada struktur data Anda, ini mungkin berdampak besar pada kinerja.
Vinci
sumber
0

Saya membuat benchmark cepat untuk membandingkan kecepatan encoding dan decoding MessagePack vs BSON. BSON lebih cepat setidaknya jika Anda memiliki array biner yang besar:

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

Menggunakan C # Newtonsoft.Json dan MessagePack oleh 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);
    }
itix
sumber
0

Nah, seperti yang dikatakan penulis, MessagePack pada awalnya dirancang untuk komunikasi jaringan sedangkan BSON dirancang untuk penyimpanan.

MessagePack kompak sementara BSON verbose. MessagePack dimaksudkan untuk menjadi hemat ruang sementara BSON dirancang untuk CURD (hemat waktu).

Yang terpenting, sistem tipe MessagePack (awalan) mengikuti pengkodean Huffman, di sini saya menggambar pohon Huffman dari MessagePack (klik tautan untuk melihat gambar) :

Pohon Huffman dari MessagePack

Jim
sumber