Есть ли в Protobuf-net встроенное сжатие для сериализации? - PullRequest
23 голосов
/ 24 августа 2011

Я проводил некоторое сравнение между BinaryFormatter и сериализатором Protobuf-net и был очень доволен тем, что нашел , но что было странным, так это то, что Protobuf-net удалось сериализовать объекты в меньший байтмассив, чем я получил бы, если бы я просто записал значение каждого свойства в массив байтов без метаданных.

Я знаю, Protobuf-net поддерживает интернирование строк, если вы установите AsReference в true, ноЯ не делаю этого в этом случае, поэтому Protobuf-net обеспечивает некоторое сжатие по умолчанию?

Вот код, который вы можете запустить, чтобы увидеть для себя:

var simpleObject = new SimpleObject
                       {
                           Id = 10,
                           Name = "Yan",
                           Address = "Planet Earth",
                           Scores = Enumerable.Range(1, 10).ToList()
                       };

using (var memStream = new MemoryStream())
{
    var binaryWriter = new BinaryWriter(memStream);
    // 4 bytes for int
    binaryWriter.Write(simpleObject.Id);      
    // 3 bytes + 1 more for string termination
    binaryWriter.Write(simpleObject.Name);    
    // 12  bytes + 1 more for string termination
    binaryWriter.Write(simpleObject.Address); 
    // 40 bytes for 10 ints
    simpleObject.Scores.ForEach(binaryWriter.Write); 

    // 61 bytes, which is what I expect
    Console.WriteLine("BinaryWriter wrote [{0}] bytes",
      memStream.ToArray().Count());
}

using (var memStream = new MemoryStream())
{
    ProtoBuf.Serializer.Serialize(memStream, simpleObject);

    // 41 bytes!
    Console.WriteLine("Protobuf serialize wrote [{0}] bytes",
      memStream.ToArray().Count());
}

РЕДАКТИРОВАТЬ: забылидобавим, класс SimpleObject выглядит следующим образом:

[Serializable]
[DataContract]
public class SimpleObject
{
    [DataMember(Order = 1)]
    public int Id { get; set; }

    [DataMember(Order = 2)]
    public string Name { get; set; }

    [DataMember(Order = 3)]
    public string Address { get; set; }

    [DataMember(Order = 4)]
    public List<int> Scores { get; set; }
}

1 Ответ

28 голосов
/ 25 августа 2011

Нет, это не так; отсутствует «сжатие» как таковое, указанное в спецификации protobuf; однако, он (по умолчанию) использует «кодировку varint» - кодирование переменной длины для целочисленных данных, что означает, что небольшие значения занимают меньше места; поэтому 0-127 берут 1 байт плюс заголовок. Обратите внимание, что varint сам по себе выглядит довольно зацикленным для отрицательных чисел, поэтому также поддерживается зигзагообразное кодирование, которое позволяет небольшим величина числам быть маленькими (в основном, это чередует положительные и отрицательные пары) .

На самом деле, в вашем случае для Scores вы также должны взглянуть на «упакованную» кодировку, для которой требуется либо [ProtoMember(4, IsPacked = true)], либо эквивалент через TypeModel в v2 (v2 поддерживает любой подход). Это позволяет избежать накладных расходов заголовка на значение, записывая один заголовок и общую длину . «Упакованный» можно использовать с варинтом / зигзагом. Существуют также кодировки фиксированной длины для сценариев, в которых вы знаете значения, вероятно, велики и непредсказуемы.

Обратите внимание: но если в ваших данных много текста, вы можете воспользоваться дополнительным прогоном через gzip или deflate; если он не , то и gzip, и deflate могут увеличить его.

Обзор формата проводов здесь ; это не очень сложно понять, и может помочь вам спланировать, как лучше оптимизировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...