Serializzazione entità performante: BSON vs MessagePack (vs JSON)


137

Di recente ho trovato MessagePack , un formato di serializzazione binario alternativo ai buffer di protocollo di Google e JSON che supera anche entrambi.

Inoltre, esiste il formato di serializzazione BSON utilizzato da MongoDB per l'archiviazione dei dati.

Qualcuno può elaborare le differenze e i vantaggi / svantaggi di BSON vs MessagePack ?


Solo per completare l'elenco dei formati di serializzazione binaria performanti: ci sono anche Gobs che saranno i successori dei buffer di protocollo di Google . Tuttavia, contrariamente a tutti gli altri formati citati, questi non sono indipendenti dalla lingua e si basano sulla riflessione integrata di Go, ma ci sono anche librerie Gobs per almeno un'altra lingua rispetto a Go.


3
Sembra principalmente un carico di pubblicità di marketing. Le prestazioni di un formato di serializzazione ["compilato"] sono dovute all'implementazione utilizzata. Mentre alcuni formati hanno intrinsecamente un sovraccarico maggiore (ad esempio JSON dato che è tutto elaborato in modo dinamico), i formati stessi non "hanno una velocità". La pagina passa quindi a "scegliere e scegliere" come si confronta da sola ... è un modo molto non imparziale. Non la mia tazza di tè.

6
Correzione: i gobbe non sono destinati a sostituire i buffer di protocollo e probabilmente non lo faranno mai. Inoltre, i Gobs sono indipendenti dalla lingua (possono essere letti / scritti in qualsiasi lingua, vedi code.google.com/p/libgob ), ma sono definiti per abbinare da vicino come Go gestisce i dati, quindi funzionano meglio con Go.
Kyle C,

6
Il collegamento ai benchmark delle prestazioni di msgpack è interrotto ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau,

Risposte:


197

// Nota che sono autore di MessagePack. Questa risposta può essere parziale.

Progettazione del formato

  1. Compatibilità con JSON

    Nonostante il suo nome, la compatibilità di BSON con JSON non è così buona rispetto a MessagePack.

    BSON ha tipi speciali come "ObjectId", "Min key", "UUID" o "MD5" (penso che questi tipi siano richiesti da MongoDB). Questi tipi non sono compatibili con JSON. Ciò significa che alcune informazioni sui tipi possono essere perse quando si convertono oggetti da BSON a JSON, ma ovviamente solo quando questi tipi speciali si trovano nell'origine BSON. Può essere uno svantaggio utilizzare sia JSON che BSON in un singolo servizio.

    MessagePack è progettato per essere convertito in modo trasparente da / a JSON.

  2. MessagePack è più piccolo di BSON

    Il formato di MessagePack è meno dettagliato di BSON. Di conseguenza, MessagePack può serializzare oggetti più piccoli di BSON.

    Ad esempio, una semplice mappa {"a": 1, "b": 2} è serializzata in 7 byte con MessagePack, mentre BSON utilizza 19 byte.

  3. BSON supporta l'aggiornamento sul posto

    Con BSON, è possibile modificare parte dell'oggetto memorizzato senza serializzare nuovamente l'intero oggetto. Supponiamo che una mappa {"a": 1, "b": 2} sia memorizzata in un file e desideri aggiornare il valore di "a" da 1 a 2000.

    Con MessagePack, 1 utilizza solo 1 byte, mentre 2000 utilizza 3 byte. Quindi "b" deve essere spostato indietro di 2 byte, mentre "b" non viene modificato.

    Con BSON, sia 1 che 2000 utilizzano 5 byte. A causa di questa verbosità, non è necessario spostare "b".

  4. MessagePack ha RPC

    MessagePack, Protocol Buffers, Thrift e Avro supportano RPC. Ma BSON no.

Queste differenze implicano che MessagePack è originariamente progettato per le comunicazioni di rete mentre BSON è progettato per gli archivi.

Implementazione e progettazione API

  1. MessagePack ha API di controllo del tipo (Java, C ++ e D)

    MessagePack supporta la digitazione statica.

    La digitazione dinamica utilizzata con JSON o BSON è utile per linguaggi dinamici come Ruby, Python o JavaScript. Ma problematico per i linguaggi statici. È necessario scrivere noiosi codici di verifica del tipo.

    MessagePack fornisce API di controllo del tipo. Converte oggetti digitati dinamicamente in oggetti tipizzati staticamente. Ecco un semplice esempio (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 ha IDL

    È correlato all'API di verifica del tipo, MessagePack supporta IDL. (le specifiche sono disponibili da: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    I buffer di protocollo e la parsimonia richiedono IDL (non supportano la tipizzazione dinamica) e forniscono un'implementazione IDL più matura.

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

    MessagePack supporta lo streaming di deserializzatori. Questa funzione è utile per le comunicazioni di rete. Ecco un esempio (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
Come si confronta MessagePack con Google Protobufs in termini di dimensioni dei dati e, di conseguenza, rispetto alle prestazioni aeree?
Ellis,

4
Il primo punto riguarda il fatto che MessagePack ha una capacità di byte grezzi che non può essere rappresentata in JSON. Quindi è lo stesso di BSON in questo senso ...

4
@lttlrck Generalmente, si presuppone che i byte grezzi siano una stringa (di solito utf-8), se non diversamente previsto e concordato su entrambi i lati del canale. msgpack è usato come formato stream / serializzazione ... e meno dettagliato di json .. sebbene anche meno leggibile dall'uomo.
Tracker1

4
"MessagePack ha API per il controllo del tipo. BSON no." Non del tutto esatto. Questo è vero anche per le implementazioni BSON in linguaggi tipicamente statici.
Brandon Black

1
MessagePack ora ha un tipo di dati BINARY, quindi l'argomento della compatibilità della deserializzazione 1-1 con JSON non è più del tutto vero.
zimbatm,

16

So che questa domanda è un po 'datata a questo punto ... Penso che sia molto importante menzionare che dipende dall'aspetto del tuo ambiente client / server.

Se si passano più volte byte senza ispezione, ad esempio con un sistema di code di messaggi o voci di registro in streaming su disco, è possibile preferire una codifica binaria per enfatizzare le dimensioni compatte. Altrimenti è un caso per caso con diversi ambienti.

Alcuni ambienti possono avere serializzazione e deserializzazione molto veloci su / da msgpack / protobuf, altri non così tanto. In generale, più basso è il livello di linguaggio / ambiente, migliore sarà la serializzazione binaria. Nei linguaggi di livello superiore (node.js, .Net, JVM) vedrai spesso che la serializzazione JSON è effettivamente più veloce. La domanda diventa allora: l'overhead della tua rete è più o meno vincolato rispetto alla tua memoria / CPU?

Per quanto riguarda i buffer msgpack vs bson vs protocol ... msgpack è il numero minimo di byte del gruppo, i buffer di protocollo sono più o meno gli stessi. BSON definisce tipi nativi più ampi rispetto agli altri due e potrebbe corrispondere meglio alla modalità dell'oggetto, ma ciò lo rende più dettagliato. I buffer di protocollo hanno il vantaggio di essere progettati per lo streaming ... il che lo rende un formato più naturale per un formato binario di trasferimento / archiviazione.

Personalmente, mi spingerei verso la trasparenza che JSON offre direttamente, a meno che non vi sia una chiara necessità di un traffico più leggero. Su HTTP con dati compressi con gzip, la differenza nell'overhead di rete è ancora meno un problema tra i formati.


6
Native MsgPack è efficace solo con le dimensioni di ProtocolBuffer in quanto la lunghezza delle chiavi (che sono sempre presenti) è breve come "a" o "b" - o è altrimenti una parte insignificante dell'intero payload . Sono sempre brevi in ​​ProtocolBuffer che utilizza un IDL / compilare per mappare i descrittori di campo agli ID. Questo è anche ciò che rende "dinamico" MsgPack, che ProtocolBuffers non è certamente ..
user2864740

2
Il punto finale è buono: gzip / deflate sono davvero buoni stanno gestendo la ridondanza delle chiavi nei casi in cui tali chiavi sono "più lunghe ma ripetute molto" (MsgPack, JSON / BSON e XML, ecc. Su molti record) ma non aiuteranno ProtocolBuffer qui. Avro esegue manualmente l'eliminazione della ridondanza delle chiavi trasmettendo lo schema separatamente.
user2864740,

4

Il test rapido mostra che JSON minimizzato è deserializzato più velocemente del MessagePack binario. Nei test Article.json è JSON minimizzato da 550kb, Article.mpack è la versione MP da 420kb. Naturalmente può essere un problema di implementazione.

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

Quindi i tempi sono:

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

Quindi lo spazio è risparmiato, ma più veloce? No.

Versioni testate:

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

7
Sicuramente dipende dalle implementazioni. I miei test con Python 2.7.3 disimballando un test.json da 489K (409K test.msgpack equivalente) mostrano che per 10.000 iterazioni simplejson2.6.2 richiede 66,7 secondi e msgpack0,2,2 richiede solo 28,8.
Giorno

2
Da dove proviene questo Article.json?
Ant6n,

gente, il codice di prova è nel mio commento sopra, cos'altro ti aspettavi, Article.json è un oggetto serializzato json dal nostro progetto. E ormai quei risultati potrebbero essere irrilevanti comunque
Oleksiy Khilkevich

14
Questo non è un confronto delle prestazioni equo, poiché JS ha implementato JSON in modo nativo in C ++, mentre msgpack in JS.
Alex Panchenko,

2
Stai cercando di far parlare MessagePack in latino meglio dei romani. JSON è nativo (C ++) in JavaScript mentre MessagePack è scritto in JavaScript, che viene interpretato. In pratica si tratta di confrontare due frammenti di codice, uno scritto in JavaScript e l'altro scritto in C ++.
Ramazan Polat,

0

Una differenza fondamentale non ancora menzionata è che BSON contiene informazioni sulle dimensioni in byte per l'intero documento e ulteriori documenti secondari nidificati.

document    ::=     int32 e_list

Ciò ha due vantaggi principali per gli ambienti con restrizioni (ad es. Incorporato) in cui dimensioni e prestazioni sono importanti.

  1. Puoi verificare immediatamente se i dati che stai per analizzare rappresentano un documento completo o se hai bisogno di richiederne altri ad un certo punto (che si tratti di una connessione o archiviazione). Poiché questa è probabilmente un'operazione asincrona, potresti già inviare una nuova richiesta prima dell'analisi.
  2. I tuoi dati potrebbero contenere interi sotto-documenti con informazioni irrilevanti per te. BSON consente di attraversare facilmente l'oggetto successivo oltre il documento secondario, utilizzando le informazioni sulla dimensione del documento secondario per saltarlo. msgpack d'altra parte contiene il numero di elementi all'interno di quella che è chiamata mappa (simile ai sotto-documenti di BSON). Sebbene si tratti indubbiamente di informazioni utili, non aiuta il parser. Dovresti comunque analizzare ogni singolo oggetto all'interno della mappa e non puoi semplicemente saltarlo. A seconda della struttura dei dati, ciò potrebbe avere un impatto enorme sulle prestazioni.

0

Ho fatto un rapido benchmark per confrontare la velocità di codifica e decodifica di MessagePack vs BSON. BSON è più veloce almeno se si dispone di array binari di grandi dimensioni:

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

Utilizzando C # Newtonsoft.Json e MessagePack di 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

Bene , come ha detto l'autore , MessagePack è stato originariamente progettato per la comunicazione di rete mentre BSON è progettato per gli archivi.

MessagePack è compatto mentre BSON è dettagliato. MessagePack è pensato per essere efficiente in termini di spazio mentre BSON è progettato per CURD (efficiente in termini di tempo).

Ancora più importante, il sistema di tipi (prefisso) di MessagePack segue la codifica Huffman, qui ho disegnato un albero Huffman di MessagePack (fare clic sul collegamento per vedere l'immagine) :

Huffman Tree of MessagePack

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.