协议缓冲区数组中浪费的字节数?

时间:2011-03-06 17:04:27

标签: c# protocol-buffers protobuf-net

我有一个像这样的协议缓冲区设置:

[ProtoContract]
Foo
{
    [ProtoMember(1)]
    Bar[] Bars;
}

单个Bar被编码为67字节的协议缓冲区。这听起来很正确,因为我知道Bar几乎只是一个64字节的数组,然后有3个字节的开销用于长度前缀。

但是,当我使用20 Bars的数组编码Foo时,需要1362个字节。 20 * 67是1340,因此只有22个字节的开销用于编码数组!

为什么这会占用太多空间?我能做些什么来减少它吗?

2 个答案:

答案 0 :(得分:5)

这个开销很简单,它需要知道20个对象中每个对象的开始和结束位置。在没有破坏格式的情况下,我可以做任何不同的事情(即做一些违反规范的事情)。

如果你真的想要血淋淋的细节:

数组或列表(如果我们排除“packed”,这里不适用)只是重复的子消息块。有两种布局可用于子消息;字符串和组。使用字符串,布局为:

[header][length][data]

其中header是有线类型和字段编号的varint编码的mash(在这种情况下为字段1的十六进制08),length是{{1}的varint编码大小数据是子对象本身。对于小对象(data小于128个字节),这通常意味着每个对象有2个字节的开销,具体取决于:字段编号(15以上的字段占用更多空间),b:数据的大小。

对于一个组,布局为:

data

其中[header][data][footer] 是wire-type和field-number的varint编码的mash(在这种情况下为字段1为hex 0B),header是子对象,{{1另一个varint mash来指示对象的结尾(在这种情况下为字段1的十六进制0C)。

群体通常不那么受欢迎,但它们的优势在于,随着data的增长,它们不会产生任何开销。对于小的字段数(小于16),开销是每个对象2个字节。当然,你需要为大字段数支付双倍的费用。

答案 1 :(得分:2)

默认情况下,数组实际上不是作为数组传递的,而是作为重复的成员传递的,它们会有更多的开销。

所以我猜你实际上每个重复的数组元素都有1个字节的开销,加上2个额外的字节开销。

使用“packed”数组可以减少开销。 protobuf-net支持:http://code.google.com/p/protobuf-net/

二进制格式的文档位于:http://code.google.com/apis/protocolbuffers/docs/encoding.html