【问题标题】:Does protobuf-net have built-in compression for serialization?protobuf-net 是否具有用于序列化的内置压缩?
【发布时间】:2011-11-02 17:30:46
【问题描述】:

我在 BinaryFormatter 和 protobuf-net 序列化程序之间进行了一些比较,并且对我的 found 非常满意,但奇怪的是 protobuf-net 设法将对象序列化为比我更小的字节数组如果我只是将每个属性的值写入一个没有任何元数据的字节数组,就会得到。

如果你将AsReference 设置为true,我知道protobuf-net 支持字符串实习,但在这种情况下我没有这样做,那么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; }
}

【问题讨论】:

    标签: serialization protobuf-net binary-serialization


    【解决方案1】:

    不,它没有; protobuf 规范中没有指定“压缩”;但是,它确实(默认情况下)使用“varint encoding” - 整数数据的可变长度编码,这意味着小值使用更少的空间;所以 0-127 需要 1 个字节加上标题。请注意,varint 本身对于负数来说非常循环,因此也支持“之字形”编码,它允许小的 magnitude 数字很小(基本上,它交错正负对)。

    实际上,对于Scores,您还应该查看“打包”编码,这需要[ProtoMember(4, IsPacked = true)] 或v2 中的TypeModel 等效(v2 支持任一方法)。这通过编写单个标头和 combined 长度来避免每个值的标头开销。 “打包”可以与 varint/zigzag 一起使用。对于您知道值可能很大且不可预测的情况,还有固定长度编码。

    另请注意:但如果您的数据包含大量文本,您可能会受益于通过 gzip 或 deflate 额外运行它;如果它没有,那么 gzip 和 deflate 都可能导致它变大。

    电汇格式概述is here;理解起来并不难,并且可以帮助您计划如何最好地进一步优化。

    【讨论】:

    • 为什么 protobuf 只为 128 个值创建 1 个字节? 8 位允许写入 256 个不同的值。
    • @tobi 它对字段编号使用“varint”编码 - 这意味着 7 位有效负载和 1 位“还有另一个字节要读取”。您继续阅读,直到 MSB 为零。
    【解决方案2】:

    至少 c++ 库确实支持写入和写入压缩流:

    https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/io/gzip_stream.h

    我不确定是否已将其移植到 .Net 实现中。

    【讨论】:

    • 在 .NET 中,您只需将任何序列化程序连接到 GZipStream 实例,然后:您就完成了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 2013-06-16
    相关资源
    最近更新 更多