Serialização de entidade com desempenho: BSON vs MessagePack (vs JSON)

137

Recentemente, encontrei o MessagePack , um formato alternativo de serialização binária para os Protocol Buffers e JSON do Google, que também superam os dois.

Também há o formato de serialização BSON usado pelo MongoDB para armazenar dados.

Alguém pode elaborar as diferenças e as desvantagens / vantagens do BSON vs MessagePack ?


Apenas para completar a lista de formatos de serialização binária de alto desempenho: Existem também Gobs que serão os sucessores dos Buffers de Protocolo do Google . No entanto, em contraste com todos os outros formatos mencionados, esses não são independentes de idioma e dependem da reflexão interna do Go. Existem também bibliotecas Gobs para pelo menos outro idioma que não o Go.

Alex
fonte
3
Parece principalmente um monte de hype de marketing. O desempenho de um formato de serialização ["compilado"] é devido à implementação usada. Embora alguns formatos tenham uma sobrecarga inerentemente maior (por exemplo, JSON, pois tudo é processado dinamicamente), os próprios formatos não "têm velocidade". A página passa a "escolher e escolher" como ela se compara ... é uma forma muito imparcial. Não é minha xícara de chá.
6
Correção: os Gobs não pretendem substituir os Buffers de Protocolo e provavelmente nunca o substituirão. Além disso, os Gobs são independentes do idioma (podem ser lidos / gravados em qualquer idioma, consulte code.google.com/p/libgob ), mas são definidos para corresponder de perto à forma como o Go lida com os dados, para que funcionem melhor com o Go.
Kyle C
6
O link para os benchmarks de desempenho do msgpack está quebrado ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau 04/10/12

Respostas:

197

// Por favor, note que sou autor do MessagePack. Esta resposta pode ser tendenciosa.

Design de formato

  1. Compatibilidade com JSON

    Apesar do nome, a compatibilidade do BSON com o JSON não é tão boa em comparação com o MessagePack.

    O BSON possui tipos especiais como "ObjectId", "Min key", "UUID" ou "MD5" (acho que esses tipos são exigidos pelo MongoDB). Esses tipos não são compatíveis com JSON. Isso significa que algumas informações de tipo podem ser perdidas quando você converte objetos de BSON para JSON, mas é claro apenas quando esses tipos especiais estão na fonte BSON. Pode ser uma desvantagem usar o JSON e o BSON no serviço único.

    O MessagePack foi projetado para ser convertido de forma transparente de / para JSON.

  2. O MessagePack é menor que o BSON

    O formato do MessagePack é menos detalhado que o BSON. Como resultado, o MessagePack pode serializar objetos menores que o BSON.

    Por exemplo, um mapa simples {"a": 1, "b": 2} é serializado em 7 bytes com o MessagePack, enquanto o BSON usa 19 bytes.

  3. BSON suporta atualização no local

    Com o BSON, você pode modificar parte do objeto armazenado sem serializar novamente todo o objeto. Vamos supor que um mapa {"a": 1, "b": 2} seja armazenado em um arquivo e você deseje atualizar o valor de "a" de 1 a 2000.

    Com o MessagePack, 1 usa apenas 1 byte, mas 2000 usa 3 bytes. Portanto, "b" deve ser movido para trás em 2 bytes, enquanto "b" não é modificado.

    Com o BSON, 1 e 2000 usam 5 bytes. Devido a essa verbosidade, você não precisa mover "b".

  4. O MessagePack possui RPC

    MessagePack, Buffers de Protocolo, Thrift e Avro suportam RPC. Mas o BSON não.

Essas diferenças implicam que o MessagePack foi originalmente projetado para comunicação em rede, enquanto o BSON foi projetado para armazenamento.

Implementação e design da API

  1. O MessagePack possui APIs de verificação de tipo (Java, C ++ e D)

    O MessagePack suporta digitação estática.

    A digitação dinâmica usada com JSON ou BSON é útil para linguagens dinâmicas como Ruby, Python ou JavaScript. Mas problemático para linguagens estáticas. Você deve escrever códigos de verificação de tipo chatos.

    O MessagePack fornece API de verificação de tipo. Ele converte objetos de tipo dinâmico em objetos de tipo estaticamente. Aqui está um exemplo simples (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. O MessagePack tem IDL

    Está relacionado à API de verificação de tipo, o MessagePack suporta IDL. (a especificação está disponível em: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Os buffers de protocolo e o Thrift requerem IDL (não suportam digitação dinâmica) e fornecem uma implementação IDL mais madura.

  2. O MessagePack possui API de streaming (Ruby, Python, Java, C ++, ...)

    O MessagePack suporta desserializadores de streaming. Esse recurso é útil para comunicação em rede. Aqui está um exemplo (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
fonte
33
Como o MessagePack se compara ao Google Protobufs em termos de tamanho dos dados e, consequentemente, desempenho no ar?
21411 Ellis
4
O primeiro ponto aborda o fato de o MessagePack ter capacidade de bytes brutos que não podem ser representados no JSON. Então, é apenas o mesmo que BSON a esse respeito ...
4
@lttlrck Geralmente, os bytes brutos são assumidos como uma string (geralmente utf-8), a menos que seja de outro modo esperado e acordado em ambos os lados do canal. O msgpack é usado como um formato de fluxo / serialização ... e menos detalhado que o json .. embora também seja menos legível por humanos.
precisa saber é o seguinte
4
"O MessagePack possui APIs de verificação de tipo. BSON não." Não é totalmente preciso. Isso também é verdade para implementações BSON em linguagens estaticamente tipadas.
Brandon Preto
1
O MessagePack agora possui um tipo de dados BINARY, portanto, o argumento da compatibilidade de desserialização 1-1 com JSON não é mais totalmente verdade.
Zimbatm
16

Eu sei que esta questão está um pouco datada neste momento ... Eu acho muito importante mencionar que depende da aparência do seu ambiente cliente / servidor.

Se você estiver passando bytes várias vezes sem inspeção, como um sistema de fila de mensagens ou entradas de log de streaming em disco, poderá preferir uma codificação binária para enfatizar o tamanho compacto. Caso contrário, é um problema caso a caso em diferentes ambientes.

Alguns ambientes podem ter serialização e desserialização muito rápidas de / para o msgpack / protobuf, outros nem tanto. Em geral, quanto mais baixo o nível do idioma / ambiente, melhor a serialização binária funcionará. Em idiomas de nível superior (node.js, .Net, JVM), você verá frequentemente que a serialização JSON é realmente mais rápida. A questão então é: a sobrecarga da sua rede é mais ou menos restrita que a sua memória / CPU?

No que diz respeito aos buffers msgpack vs bson vs protocol ... msgpack é o mínimo de bytes do grupo, sendo os buffers de protocolo os mesmos. O BSON define tipos nativos mais amplos que os outros dois e pode ser uma melhor correspondência para o modo de objeto, mas isso o torna mais detalhado. Os buffers de protocolo têm a vantagem de serem projetados para transmitir ... o que o torna um formato mais natural para um formato de transferência / armazenamento binário.

Pessoalmente, eu me inclinaria para a transparência que o JSON oferece diretamente, a menos que haja uma clara necessidade de tráfego mais leve. Sobre HTTP com dados compactados em gz, a diferença na sobrecarga da rede é ainda menos problemática entre os formatos.

Tracker1
fonte
6
O MsgPack nativo é eficiente apenas com ProtocolBuffers em termos de tamanho, pois o comprimento das chaves (que estão sempre presentes no texto) é curto como "a" ou "b" - ou, de outra forma, é uma parte insignificante de toda a carga útil . Eles são sempre curtos em ProtocolBuffers, que usa uma IDL / compilação para mapear descritores de campo para ids. Este é também o que faz MsgPack "dinâmico", que ProtocolBuffers não é certamente ..
user2864740
2
O ponto final é bom: gzip / deflate são realmente bons, lidam com redundância de chaves nos casos em que essas chaves são "mais longas, mas repetidas" (MsgPack, JSON / BSON e XML, etc. em muitos registros), mas não ajudam ProtocolBuffers em tudo aqui. O Avro elimina manualmente a redundância de chave transmitindo o esquema separadamente.
user2864740
4

O teste rápido mostra que o JSON minificado é desserializado mais rapidamente que o MessagePack binário. Nos testes, Article.json é JSON compactado em 550kb, Article.mpack é a versão MP em 420kb. Pode ser uma questão de implementação, é claro.

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

Então os tempos são:

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

Então o espaço é economizado, mas mais rápido? Não.

Versões testadas:

Anarki:Downloads oleksii$ node --version
v0.8.12
Anarki:Downloads oleksii$ npm list msgpack
/Users/oleksii
└── [email protected]  
Oleksiy Khilkevich
fonte
7
Definitivamente depende das implementações. Meus testes com o Python 2.7.3 descompactando um 489K test.json (equivalente a 409K test.msgpack) mostram que, para 10.000 iterações, o simplejson2.6.2 leva 66.7 segundos e o msgpack0.2.2 leva apenas 28.8.
Dia
2
De onde veio esse Article.json?
Ant6n 22/02
pessoal, o código de teste está no meu comentário acima, o que mais você esperava, Article.json é um objeto serializado de json do nosso projeto. E agora esses resultados pode ser irrelevante qualquer maneira
Oleksiy Khilkevich
14
Esta não é uma comparação justa de desempenho, pois o JSON implementou JSON nativamente em C ++, enquanto o msgpack em JS.
precisa saber é o seguinte
2
Você está tentando fazer o MessagePack falar latim melhor que os romanos. JSON é nativo (C ++) para JavaScript enquanto MessagePack é gravado em JavaScript, que é interpretado. Isso é basicamente comparando dois trechos de código, um escrito em JavaScript e outro escrito em C ++.
Ramazan Polat
0

Uma diferença importante ainda não mencionada é que o BSON contém informações de tamanho em bytes para o documento inteiro e outros sub-documentos aninhados.

document    ::=     int32 e_list

Isso tem dois grandes benefícios para ambientes restritos (por exemplo, incorporados), onde tamanho e desempenho são importantes.

  1. Você pode verificar imediatamente se os dados que você irá analisar representam um documento completo ou se precisará solicitar mais em algum momento (seja de alguma conexão ou armazenamento). Como essa é provavelmente uma operação assíncrona, você já pode enviar uma nova solicitação antes de analisar.
  2. Seus dados podem conter sub-documentos inteiros com informações irrelevantes para você. O BSON permite que você passe facilmente para o próximo objeto após o sub-documento usando as informações de tamanho do sub-documento para ignorá-lo. Por outro lado, o msgpack contém o número de elementos dentro do que é chamado de mapa (semelhante aos sub-documentos do BSON). Embora essas informações sejam indubitavelmente úteis, elas não ajudam o analisador. Você ainda teria que analisar cada objeto dentro do mapa e não pode simplesmente ignorá-lo. Dependendo da estrutura dos seus dados, isso pode ter um enorme impacto no desempenho.
Vinci
fonte
0

Fiz benchmark rápido para comparar a velocidade de codificação e decodificação do MessagePack vs BSON. BSON é mais rápido, pelo menos, se você tiver grandes matrizes binárias:

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

Usando C # Newtonsoft.Json e MessagePack por 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
fonte
0

Bem, como o autor disse, o MessagePack foi originalmente projetado para comunicação em rede, enquanto o BSON foi projetado para armazenamento.

O MessagePack é compacto enquanto o BSON é detalhado. O MessagePack deve economizar espaço, enquanto o BSON é projetado para CURD (economia de tempo).

Mais importante, o sistema de tipos do MessagePack (prefixo) segue a codificação Huffman, aqui eu desenhei uma árvore Huffman do MessagePack (clique no link para ver a imagem) :

Árvore Huffman de MessagePack

Jim
fonte