我在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; }
}
答案 0 :(得分:28)
不,不; protobuf规范中没有指定“压缩”;但是,它(默认情况下)使用“varint encoding” - 整数数据的可变长度编码,这意味着小值使用更少的空间;所以0-127需要1个字节加上标题。请注意,varint 本身对于负数而言非常循环,因此也支持“zigzag”编码,这允许小幅度数字很小(基本上,它交错正负对)。
实际上,对于Scores
的情况,您还应该查看“压缩”编码,这需要[ProtoMember(4, IsPacked = true)]
或v2中等效的TypeModel
(v2支持这两种方法)。通过编写单个标头和组合长度,可以避免每个标头的开销。 “Packed”可以与varint / zigzag一起使用。对于您知道值可能很大且不可预测的情况,还有固定长度的编码。
另请注意:但如果您的数据包含大量文本,则可以通过gzip或deflate运行它来获益;如果不,则gzip和deflate都会导致它变大。
电汇格式is here的概述;理解并不是很棘手,可能会帮助您规划如何进一步优化。
答案 1 :(得分:0)
至少c ++库确实支持在压缩流之间进行写操作:
https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/io/gzip_stream.h
我不确定是否已将其移植到.Net实现中。