我目前正在研究protocol buffers的文档。 Varints描述为:
varint中的每个字节(最后一个字节除外)具有最重要的字节 bit(msb)set - 这表示还有其他字节。 每个字节的低7位用于存储二进制补码 以7位为单位表示数字,最不重要 小组第一。
我的问题是为什么人们会选择在每个字节上丢失一位的表示?这种方法有什么好处?
答案 0 :(得分:13)
实际上,绝大多数整数值都很小。即使在您希望某个值有时会非常大的情况下,从而使其成为32位甚至64位,也可能通常很小,因为从统计上讲,大多数物理量都是如此遵循幂律分布。因此,如果较小的值可以存储在较少的字节中,那么如果较大的值需要额外的字节就可以了。
关于唯一没有受益的整数是哈希或随机生成的ID号,它们实际上并不代表数量,而只是一个字符串。对于这些,您应该使用Protobufs'fixed32
或fixed64
类型。
请注意,varint编码节省了线路上的空间,但实际上相对较慢,因为它需要大量的分支来编码/解码。当然,它并不像文本编码那么慢,但作为二进制格式,它并不是那么好。这就是为什么Cap'n Proto决定撤销这个决定并在线上放置固定宽度的一个原因的原因之一。 Cap'n Proto还包括一个可选的“打包”算法,它压缩零值字节,产生与Protobuf类似的消息大小,但通常更快,因为算法使用较少的分支。
(披露:我是Cap'n Proto的作者,也是Google发布的大部分Protobuf代码。)
答案 1 :(得分:11)
节省空间/带宽,例如许多编程语言和协议都有固定大小的数据类型。例如uint8_t,uint16_t,uint32_t等等,无论值多大,这些都会占用固定的字节数。例如如果将值2
存储在uint32_t中,则占用4个字节。
使用protobuf中使用的varint等编码,小值可以占用较小的空间,值2
只需要在线上传输1个字节的空间,同时仍然足够灵活限制可以使用的值的范围。
如果小值比大值更常见,这通常是一个净胜利 - 通常就是这种情况。
答案 2 :(得分:3)
使用这种方法,如果你有很多小数字,你可以节省相当多的内存空间和/或传输时间,同时仍然能够代表任意大的数字。例如,您不必为每个数字分配8个字节。如果您有许多小数字,那么大多数这些字节都会为零。对于8字节数字,您的值限制为(2 ^ 64 - 1),如果您的值为2 ^ 64或更高,则需要执行一些特殊操作。
使用varint编码,通常可以节省大量内存,并且能够表示任意大小的数字。
答案 3 :(得分:2)
这是一个权衡,取决于您要发送的典型数字。
int32, int64, uint32, uint64, sint32, sint64, bool, enum
)fixed32, sfixed32, float, fixed64, sfixed64, double
)这里是sint64
,int64
和fixed64
用于各种数字的字节的比较:
[ RUN ] ProtobufOutputStream.printNumberOfBytesOnWireForInts
number sint64 int64 fixed64
0 2 2 9
1 2 2 9
-1 2 11 9
63 2 2 9
-63 2 11 9
64 3 2 9
-64 2 11 9
10000 4 3 9
-10000 4 11 9
9223372036854775807 11 10 9
-9223372036854775808 11 11 9