我有一个像这样的协议缓冲区设置:
[ProtoContract]
Foo
{
[ProtoMember(1)]
Bar[] Bars;
}
单个Bar被编码为67字节的协议缓冲区。这听起来很正确,因为我知道Bar几乎只是一个64字节的数组,然后有3个字节的开销用于长度前缀。
但是,当我使用20 Bars的数组编码Foo时,需要1362个字节。 20 * 67是1340,因此只有22个字节的开销用于编码数组!
为什么这会占用太多空间?我能做些什么来减少它吗?
答案 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