Performant Entity Serialization: BSON gegen MessagePack (gegen JSON)


137

Kürzlich habe ich MessagePack gefunden , ein alternatives binäres Serialisierungsformat zu Googles Protokollpuffern und JSON, das auch beide übertrifft.

Es gibt auch das BSON- Serialisierungsformat, das von MongoDB zum Speichern von Daten verwendet wird.

Kann jemand die Unterschiede und die Nachteile von BSON gegenüber MessagePack erläutern ?


Um die Liste der performanten binären Serialisierungsformate zu vervollständigen: Es gibt auch Gobs, die der Nachfolger von Googles Protokollpuffern sein werden . Im Gegensatz zu allen anderen genannten Formaten, die nicht sprachunabhängig sind und auf Go's integrierter Reflexion beruhen, gibt es auch Gobs-Bibliotheken für zumindest eine andere Sprache als Go.


3
Scheint meistens wie eine Menge Marketing-Hype. Die Leistung eines ["kompilierten"] Serialisierungsformats hängt von der verwendeten Implementierung ab. Während einige Formate von Natur aus mehr Overhead haben (z. B. JSON, da alles dynamisch verarbeitet wird), haben Formate selbst keine "Geschwindigkeit". Die Seite fährt dann fort, "auszuwählen", wie sie sich selbst vergleicht ... es ist eine sehr unvoreingenommene Art und Weise. Nicht mein Fall.

6
Korrektur: Gobs sind nicht dazu gedacht, Protokollpuffer zu ersetzen, und werden es wahrscheinlich nie tun. Gobs sind auch sprachunabhängig (sie können in jeder Sprache gelesen / geschrieben werden, siehe code.google.com/p/libgob ), aber sie sind so definiert, dass sie genau mit dem Umgang von Go mit Daten übereinstimmen, sodass sie am besten mit Go funktionieren.
Kyle C

6
Der Link zu den Leistungsbenchmarks von msgpack ist unterbrochen ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau

Antworten:


197

// Bitte beachte, dass ich Autor von MessagePack bin. Diese Antwort kann voreingenommen sein.

Format Design

  1. Kompatibilität mit JSON

    Trotz des Namens ist die Kompatibilität von BSON mit JSON im Vergleich zu MessagePack nicht so gut.

    BSON hat spezielle Typen wie "ObjectId", "Min Key", "UUID" oder "MD5" (ich denke, diese Typen werden von MongoDB benötigt). Diese Typen sind nicht mit JSON kompatibel. Das bedeutet, dass einige Typinformationen verloren gehen können, wenn Sie Objekte von BSON in JSON konvertieren, aber natürlich nur, wenn sich diese speziellen Typen in der BSON-Quelle befinden. Es kann ein Nachteil sein, sowohl JSON als auch BSON in einem einzigen Dienst zu verwenden.

    MessagePack kann transparent von / nach JSON konvertiert werden.

  2. MessagePack ist kleiner als BSON

    Das Format von MessagePack ist weniger ausführlich als das von BSON. Infolgedessen kann MessagePack Objekte serialisieren, die kleiner als BSON sind.

    Beispielsweise wird eine einfache Zuordnung {"a": 1, "b": 2} mit MessagePack in 7 Bytes serialisiert, während BSON 19 Bytes verwendet.

  3. BSON unterstützt die direkte Aktualisierung

    Mit BSON können Sie einen Teil des gespeicherten Objekts ändern, ohne das gesamte Objekt neu zu serialisieren. Angenommen, eine Karte {"a": 1, "b": 2} ist in einer Datei gespeichert und Sie möchten den Wert von "a" von 1 auf 2000 aktualisieren.

    Bei MessagePack verwendet 1 nur 1 Byte, 2000 jedoch 3 Byte. "B" muss also um 2 Bytes rückwärts verschoben werden, während "b" nicht geändert wird.

    Mit BSON verwenden sowohl 1 als auch 2000 5 Bytes. Aufgrund dieser Ausführlichkeit müssen Sie "b" nicht bewegen.

  4. MessagePack hat RPC

    MessagePack, Protokollpuffer, Thrift und Avro unterstützen RPC. Aber BSON nicht.

Diese Unterschiede implizieren, dass MessagePack ursprünglich für die Netzwerkkommunikation entwickelt wurde, während BSON für Speicher konzipiert ist.

Implementierung und API-Design

  1. MessagePack verfügt über APIs zur Typprüfung (Java, C ++ und D).

    MessagePack unterstützt die statische Typisierung.

    Die mit JSON oder BSON verwendete dynamische Typisierung ist nützlich für dynamische Sprachen wie Ruby, Python oder JavaScript. Aber problematisch für statische Sprachen. Sie müssen langweilige Codes für die Typprüfung schreiben.

    MessagePack bietet eine API zur Typprüfung. Es konvertiert dynamisch typisierte Objekte in statisch typisierte Objekte. Hier ist ein einfaches Beispiel (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 hat IDL

    MessagePack unterstützt IDL. (Die Spezifikation ist verfügbar unter: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Protokollpuffer und Thrift erfordern IDL (unterstützen keine dynamische Typisierung) und bieten eine ausgereiftere IDL-Implementierung.

  2. MessagePack verfügt über eine Streaming-API (Ruby, Python, Java, C ++, ...)

    MessagePack unterstützt Streaming-Deserializer. Diese Funktion ist nützlich für die Netzwerkkommunikation. Hier ist ein Beispiel (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
Wie vergleicht sich MessagePack mit Google Protobufs hinsichtlich der Datengröße und folglich der Funkleistung?
Ellis

4
Der erste Punkt beschönigt die Tatsache, dass MessagePack über Raw-Bytes verfügt, die in JSON nicht dargestellt werden können. Also ist es in dieser Hinsicht genauso wie BSON ...

4
@lttlrck Im Allgemeinen wird angenommen, dass die Rohbytes eine Zeichenfolge sind (normalerweise utf-8), sofern auf beiden Seiten des Kanals nichts anderes erwartet und vereinbart wird. msgpack wird als Stream- / Serialisierungsformat verwendet ... und weniger ausführlich als json ... obwohl es auch weniger lesbar ist.
Tracker1

4
"MessagePack verfügt über APIs zur Typprüfung. BSON nicht." Nicht ganz genau. Dies gilt auch für BSON-Implementierungen in statisch typisierten Sprachen.
Brandon Black

1
MessagePack hat jetzt den Datentyp BINARY, sodass das Argument der 1-1-De-Serialisierungskompatibilität mit JSON nicht mehr ganz richtig ist.
Simbatm

16

Ich weiß, dass diese Frage an dieser Stelle etwas veraltet ist ... Ich denke, es ist sehr wichtig zu erwähnen, dass es davon abhängt, wie Ihre Client / Server-Umgebung aussieht.

Wenn Sie Bytes mehrmals ohne Überprüfung übergeben, z. B. mit einem Nachrichtenwarteschlangensystem oder dem Streamen von Protokolleinträgen auf die Festplatte, bevorzugen Sie möglicherweise eine Binärcodierung, um die kompakte Größe hervorzuheben. Ansonsten ist es ein Fall-zu-Fall-Problem mit verschiedenen Umgebungen.

Einige Umgebungen können eine sehr schnelle Serialisierung und Deserialisierung zu / von msgpack / protobufs haben, andere weniger. Im Allgemeinen funktioniert die binäre Serialisierung umso besser, je niedriger die Sprache / Umgebung ist. In höheren Sprachen (node.js, .Net, JVM) werden Sie häufig feststellen, dass die JSON-Serialisierung tatsächlich schneller ist. Die Frage wird dann, ob Ihr Netzwerk-Overhead mehr oder weniger eingeschränkt ist als Ihr Speicher / Ihre CPU.

In Bezug auf msgpack vs bson vs Protokollpuffer ... msgpack ist das kleinste Byte der Gruppe, wobei die Protokollpuffer ungefähr gleich sind. BSON definiert breitere native Typen als die beiden anderen und passt möglicherweise besser zu Ihrem Objektmodus, dies macht ihn jedoch ausführlicher. Protokollpuffer haben den Vorteil, dass sie für das Streaming ausgelegt sind ... was sie zu einem natürlicheren Format für ein binäres Übertragungs- / Speicherformat macht.

Persönlich würde ich mich auf die Transparenz stützen, die JSON direkt bietet, es sei denn, es besteht eindeutig ein Bedarf an weniger Verkehr. Bei HTTP mit komprimierten Daten ist der Unterschied im Netzwerk-Overhead zwischen den Formaten noch weniger problematisch.


6
Native MsgPack ist nur mit ProtocolBuffers in Bezug auf die Größe effizient, da die Länge der Schlüssel (bei denen es sich immer um Text handelt) kurz ist, z. B. "a" oder "b" - oder auf andere Weise einen unbedeutenden Teil der gesamten Nutzlast darstellt . Sie sind in ProtocolBuffers, die eine IDL / Kompilierung verwenden, um Felddeskriptoren IDs zuzuordnen, immer kurz. Dies ist auch, was MsgPack "dynamisch" macht, was ProtocolBuffers mit Sicherheit nicht ist ..
user2864740

2
Der Endpunkt ist jedoch gut: gzip / deflate sind wirklich gut, wenn es darum geht, die Redundanz von Schlüsseln in Fällen zu behandeln, in denen solche Schlüssel "länger, aber viel wiederholt" sind (MsgPack, JSON / BSON und XML usw. über viele Datensätze hinweg), aber nicht helfen ProtocolBuffers hier überhaupt. Avro führt die Beseitigung der Schlüsselredundanz manuell durch, indem das Schema separat übertragen wird.
Benutzer2864740

4

Schnelltest zeigt, dass minimiertes JSON schneller deserialisiert wird als binäres MessagePack. In den Tests ist Article.json 550 KB minimiertes JSON, Article.mpack ist 420 KB MP-Version davon. Kann natürlich ein Implementierungsproblem sein.

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

Die Zeiten sind also:

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

So wird Platz gespart, aber schneller? Nein.

Getestete Versionen:

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

7
Kommt definitiv auf die Implementierungen an. Meine Tests mit Python 2.7.3 beim Entpacken einer 489K test.json (409K äquivalente test.msgpack) zeigen, dass für 10.000 Iterationen simplejson2.6.2 66,7 Sekunden und msgpack0.2.2 nur 28,8 Sekunden dauert.
Tag

2
Woher kommt dieser Article.json?
Ant6n

Leute, Testcode ist in meinem Kommentar oben, was haben Sie sonst noch erwartet, Article.json ist ein json-serialisiertes Objekt aus unserem Projekt. Und jetzt könnten diese Ergebnisse sowieso irrelevant sein
Oleksiy Khilkevich

14
Dies ist kein fairer Leistungsvergleich, da JS JSON nativ in C ++ implementiert hat, während msgpack in JS.
Alex Panchenko

2
Sie versuchen, MessagePack dazu zu bringen, Latein besser zu sprechen als Römer. JSON ist in JavaScript nativ (C ++), während MessagePack in JavaScript geschrieben ist, das interpretiert wird. Hierbei werden im Grunde zwei Codefragmente verglichen, eines in JavaScript und eines in C ++.
Ramazan Polat

0

Ein wesentlicher Unterschied, der noch nicht erwähnt wurde, besteht darin, dass BSON Größeninformationen in Bytes für das gesamte Dokument und weitere verschachtelte Unterdokumente enthält.

document    ::=     int32 e_list

Dies hat zwei Hauptvorteile für eingeschränkte Umgebungen (z. B. eingebettet), in denen Größe und Leistung wichtig sind.

  1. Sie können sofort überprüfen, ob die zu analysierenden Daten ein vollständiges Dokument darstellen oder ob Sie irgendwann mehr anfordern müssen (sei es von einer Verbindung oder einem Speicher). Da dies höchstwahrscheinlich eine asynchrone Operation ist, senden Sie möglicherweise bereits vor dem Parsen eine neue Anforderung.
  2. Ihre Daten können ganze Unterdokumente mit irrelevanten Informationen für Sie enthalten. Mit BSON können Sie problemlos zum nächsten Objekt nach dem Unterdokument wechseln, indem Sie die Größeninformationen des Unterdokuments verwenden, um es zu überspringen. msgpack hingegen enthält die Anzahl der Elemente in einer sogenannten Map (ähnlich den BSON-Unterdokumenten). Dies sind zweifellos nützliche Informationen, die dem Parser jedoch nicht helfen. Sie müssten immer noch jedes einzelne Objekt in der Karte analysieren und können es nicht einfach überspringen. Abhängig von der Struktur Ihrer Daten kann dies einen großen Einfluss auf die Leistung haben.

0

Ich habe einen schnellen Benchmark erstellt, um die Codierungs- und Decodierungsgeschwindigkeit von MessagePack mit BSON zu vergleichen. BSON ist zumindest dann schneller, wenn Sie große binäre Arrays haben:

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

Verwenden von C # Newtonsoft.Json und MessagePack von 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

Nun, wie der Autor sagte, ist MessagePack ursprünglich für die Netzwerkkommunikation konzipiert, während BSON für die Speicherung konzipiert ist.

MessagePack ist kompakt, während BSON ausführlich ist. MessagePack soll platzsparend sein, während BSON für CURD (zeiteffizient) ausgelegt ist.

Am wichtigsten ist, dass das Typsystem (Präfix) von MessagePack der Huffman-Codierung folgt. Hier habe ich einen Huffman-Baum von MessagePack gezeichnet (klicken Sie auf den Link, um das Bild zu sehen):

Huffman-Baum von MessagePack

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.