Sérialisation d'entités performantes: BSON vs MessagePack (vs JSON)


137

Récemment, j'ai trouvé MessagePack , un format de sérialisation binaire alternatif aux tampons de protocole de Google et JSON qui surpasse également les deux.

Il existe également le format de sérialisation BSON utilisé par MongoDB pour stocker des données.

Quelqu'un peut-il expliquer les différences et les inconvénients / avantages de BSON par rapport à MessagePack ?


Juste pour compléter la liste des formats de sérialisation binaires performants: il existe également des Gobs qui vont succéder aux Protocol Buffers de Google . Cependant, contrairement à tous les autres formats mentionnés, ceux-ci ne sont pas indépendants du langage et reposent sur la réflexion intégrée de Go, il existe également des bibliothèques Gobs pour au moins un autre langage que Go.


3
Cela ressemble surtout à un battage médiatique marketing. Les performances d'un format de sérialisation ["compilé"] sont dues à l'implémentation utilisée. Alors que certains formats ont intrinsèquement plus de surcharge (par exemple JSON car tout est traité dynamiquement), les formats eux-mêmes n'ont pas de «vitesse». La page continue ensuite à "choisir et choisir" comment elle se compare ... c'est une mode très non impartiale. Pas ma tasse de thé.

6
Correction: les Gobs ne sont pas destinés à remplacer les tampons de protocole, et ne le seront probablement jamais. De plus, les Gobs sont indépendants du langage (ils peuvent être lus / écrits dans n'importe quelle langue, voir code.google.com/p/libgob ), mais ils sont définis pour correspondre étroitement à la manière dont Go traite les données, de sorte qu'ils fonctionnent mieux avec Go.
Kyle C

6
Le lien vers les benchmarks de performance de msgpack est rompu ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau

Réponses:


197

// Veuillez noter que je suis l'auteur de MessagePack. Cette réponse peut être biaisée.

Conception de format

  1. Compatibilité avec JSON

    Malgré son nom, la compatibilité de BSON avec JSON n'est pas aussi bonne que MessagePack.

    BSON a des types spéciaux comme "ObjectId", "Min key", "UUID" ou "MD5" (je pense que ces types sont requis par MongoDB). Ces types ne sont pas compatibles avec JSON. Cela signifie que certaines informations de type peuvent être perdues lorsque vous convertissez des objets de BSON en JSON, mais bien sûr uniquement lorsque ces types spéciaux sont dans la source BSON. Il peut être désavantageux d'utiliser à la fois JSON et BSON dans un seul service.

    MessagePack est conçu pour être converti de manière transparente de / vers JSON.

  2. MessagePack est plus petit que BSON

    Le format de MessagePack est moins détaillé que BSON. En conséquence, MessagePack peut sérialiser des objets plus petits que BSON.

    Par exemple, une simple carte {"a": 1, "b": 2} est sérialisée en 7 octets avec MessagePack, tandis que BSON utilise 19 octets.

  3. BSON prend en charge la mise à jour sur place

    Avec BSON, vous pouvez modifier une partie de l'objet stocké sans re-sérialiser l'ensemble de l'objet. Supposons qu'une carte {"a": 1, "b": 2} soit stockée dans un fichier et que vous souhaitiez mettre à jour la valeur de "a" de 1 à 2000.

    Avec MessagePack, 1 utilise seulement 1 octet mais 2000 utilise 3 octets. Donc "b" doit être reculé de 2 octets, tandis que "b" n'est pas modifié.

    Avec BSON, 1 et 2000 utilisent 5 octets. En raison de cette verbosité, vous n'avez pas à déplacer "b".

  4. MessagePack a RPC

    MessagePack, Protocol Buffers, Thrift et Avro prennent en charge RPC. Mais BSON ne le fait pas.

Ces différences impliquent que MessagePack est à l'origine conçu pour la communication réseau tandis que BSON est conçu pour les stockages.

Implémentation et conception d'API

  1. MessagePack a des API de vérification de type (Java, C ++ et D)

    MessagePack prend en charge la saisie statique.

    Le typage dynamique utilisé avec JSON ou BSON est utile pour les langages dynamiques comme Ruby, Python ou JavaScript. Mais gênant pour les langages statiques. Vous devez écrire des codes de vérification de type ennuyeux.

    MessagePack fournit une API de vérification de type. Il convertit les objets typés dynamiquement en objets typés statiquement. Voici un exemple simple (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 a IDL

    Il est lié à l'API de vérification de type, MessagePack prend en charge IDL. (la spécification est disponible sur: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Protocol Buffers et Thrift nécessitent IDL (ne prennent pas en charge le typage dynamique) et fournissent une implémentation IDL plus mature.

  2. MessagePack a une API de streaming (Ruby, Python, Java, C ++, ...)

    MessagePack prend en charge les désérialiseurs de streaming. Cette fonction est utile pour la communication réseau. Voici un exemple (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
Comment MessagePack se compare-t-il à Google Protobufs en termes de taille de données et, par conséquent, de performances en direct?
Ellis

4
Le premier point passe sous silence le fait que MessagePack a une capacité d'octets bruts qui ne peut pas être représentée dans JSON. Donc, c'est exactement la même chose que BSON à cet égard ...

4
@lttlrck Généralement, les octets bruts sont supposés être une chaîne (généralement utf-8), sauf indication contraire et accord des deux côtés du canal. msgpack est utilisé comme format de flux / sérialisation ... et moins verbeux que json ... mais aussi moins lisible par l'homme.
Tracker1

4
"MessagePack a des API de vérification de type. BSON n'en a pas." Pas tout à fait exact. Cela est également vrai pour les implémentations BSON dans les langages à typage statique.
Brandon Black

1
MessagePack a maintenant un type de données BINARY, donc l'argument de la compatibilité de désérialisation 1-1 avec JSON n'est plus entièrement vrai.
zimbatm

16

Je sais que cette question est un peu datée à ce stade ... Je pense qu'il est très important de mentionner que cela dépend de ce à quoi ressemble votre environnement client / serveur.

Si vous passez des octets plusieurs fois sans inspection, par exemple avec un système de file d'attente de messages ou des entrées de journal en streaming sur disque, vous préférerez peut-être un codage binaire pour souligner la taille compacte. Sinon, c'est un problème au cas par cas avec différents environnements.

Certains environnements peuvent avoir une sérialisation et une désérialisation très rapides vers / depuis msgpack / protobuf, d'autres pas tellement. En général, plus le langage / environnement est de bas niveau, meilleure sera la sérialisation binaire. Dans les langages de niveau supérieur (node.js, .Net, JVM), vous verrez souvent que la sérialisation JSON est en fait plus rapide. La question est alors de savoir si la surcharge de votre réseau est plus ou moins contrainte que votre mémoire / processeur?

En ce qui concerne msgpack vs bson vs tampons de protocole ... msgpack est le moins d'octets du groupe, les tampons de protocole étant à peu près les mêmes. BSON définit des types natifs plus larges que les deux autres, et peut être une meilleure correspondance avec votre mode objet, mais cela le rend plus verbeux. Les tampons de protocole ont l'avantage d'être conçus pour diffuser ... ce qui en fait un format plus naturel pour un format de transfert / stockage binaire.

Personnellement, je pencherais vers la transparence qu'offre directement JSON, à moins qu'il n'y ait un besoin clair de trafic plus léger. Sur HTTP avec des données gzippées, la différence de surcharge du réseau est encore moins un problème entre les formats.


6
Natif MsgPack est seulement efficace avec la taille-sage protocol buffers que la longueur des clés (qui sont toujours présents texte) sont courts tels que « a » ou « b » - ou sont autrement une partie non négligeable de l'ensemble de la charge utile . Ils sont toujours courts dans ProtocolBuffers qui utilise un IDL / compile pour mapper les descripteurs de champ aux identifiants. C'est aussi ce qui rend MsgPack "dynamique", ce que ProtocolBuffers n'est certainement pas ..
user2864740

2
Le point final est bon cependant: gzip / deflate sont vraiment bons gèrent la redondance des clés dans les cas où ces clés sont "plus longues mais répétées beaucoup" (MsgPack, JSON / BSON et XML, etc. sur de nombreux enregistrements) mais n'aideront pas ProtocolBuffers du tout ici. Avro élimine manuellement la redondance des touches en transmettant le schéma séparément.
user2864740

4

Un test rapide montre que JSON minifié est désérialisé plus rapidement que MessagePack binaire. Dans les tests, Article.json est un JSON minifié de 550 Ko, Article.mpack en est une version MP de 420 Ko. Cela peut bien sûr être un problème de mise en œuvre.

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

Les temps sont donc:

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

L'espace est donc économisé, mais plus rapidement? Non.

Versions testées:

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

7
Dépend définitivement des implémentations. Mes tests avec Python 2.7.3 décompressant un test.json de 489K (test.msgpack équivalent à 409K) montrent que pour 10000 itérations, simplejson2.6.2 prend 66,7 secondes et msgpack0.2.2 ne prend que 28,8.
Jour

2
D'où vient cet Article.json?
Ant6n

les gens, le code de test est dans mon commentaire ci-dessus, à quoi vous attendiez-vous, Article.json est un objet json sérialisé de notre projet. Et à présent, ces résultats pourraient être sans intérêt de toute façon
Oleksiy Khilkevich

14
Ce n'est pas une comparaison équitable des performances, car JS a implémenté JSON nativement en C ++, tandis que msgpack en JS.
Alex Panchenko le

2
Vous essayez de faire en sorte que MessagePack parle mieux le latin que les romains. JSON est natif (C ++) de JavaScript tandis que MessagePack est écrit en JavaScript, qui est interprété. Il s'agit essentiellement de comparer deux extraits de code, l'un écrit en JavaScript et l'autre écrit en C ++.
Ramazan Polat du

0

Une différence clé non encore mentionnée est que BSON contient des informations de taille en octets pour l'ensemble du document et d'autres sous-documents imbriqués.

document    ::=     int32 e_list

Cela présente deux avantages majeurs pour les environnements restreints (par exemple embarqués) où la taille et les performances sont importantes.

  1. Vous pouvez immédiatement vérifier si les données que vous allez analyser représentent un document complet ou si vous allez avoir besoin d'en demander plus à un moment donné (que ce soit à partir d'une connexion ou d'un stockage). Comme il s'agit probablement d'une opération asynchrone, vous pouvez déjà envoyer une nouvelle demande avant l'analyse.
  2. Vos données peuvent contenir des sous-documents entiers avec des informations non pertinentes pour vous. BSON vous permet de passer facilement à l'objet suivant après le sous-document en utilisant les informations de taille du sous-document pour l'ignorer. msgpack d'autre part contient le nombre d'éléments à l'intérieur de ce qu'on appelle une carte (similaire aux sous-documents de BSON). Bien que ce soit sans aucun doute une information utile, cela n'aide pas l'analyseur. Vous devrez toujours analyser chaque objet à l'intérieur de la carte et ne pouvez pas simplement l'ignorer. Selon la structure de vos données, cela peut avoir un impact considérable sur les performances.

0

J'ai fait un benchmark rapide pour comparer la vitesse d'encodage et de décodage de MessagePack vs BSON. BSON est au moins plus rapide si vous avez de grands tableaux binaires:

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

Utilisation de C # Newtonsoft.Json et MessagePack par 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

Eh bien, comme l'auteur l'a dit, MessagePack est à l'origine conçu pour la communication réseau tandis que BSON est conçu pour les stockages.

MessagePack est compact tandis que BSON est verbeux. MessagePack est conçu pour être peu encombrant tandis que BSON est conçu pour CURD (time-efficient).

Plus important encore, le système de type de MessagePack (préfixe) suit le codage Huffman, ici j'ai dessiné un arbre Huffman de MessagePack (cliquez sur le lien pour voir l'image) :

Arbre de message de Huffman

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.