在Google Protocol Buffers中使用int32而不是sint32是不是很好?

Dan*_*Lew 19 primitive protocol-buffers

我最近一直在阅读Google Protocol Buffers,它允许在消息中使用各种标量值类型.

根据他们的文档,有三种类型的可变长度整数原语- int32,uint32sint32.在他们的文档中,他们指出int32"编码负数的效率低 - 如果您的字段可能有负值,请sint32改用." 但是如果你有一个没有负数的字段,我认为uint32将是一个比使用更好的类型int32(由于额外的位和处理负数的CPU成本降低).

那么什么时候int32使用是一个很好的标量?文档是否意味着只有当您很少得到负数时它才是最有效的?或者它总是优先使用,sint32uint32取决于该领域的内容?

(同样的问题也适用于这些标量的64位版本,以及:int64,uint64,和sint64,但我离开他们出了问题描述为可读性的缘故.)

Mic*_*urr 26

我不熟悉Google Protocol Buffers,但我对文档的解释是:

  • 使用uint32如果该值不能为负
  • 使用,sint32如果该值几乎可能是否定为否定(对于某些模糊定义"很可能")
  • int32如果值可能为负,则使用,但这比值为正值的可能性小得多(例如,如果应用程序有时使用-1表示错误或"未知"值,这是一种相对不常见的情况)

以下是文档对编码的说法(http://code.google.com/apis/protocolbuffers/docs/encoding.html#types):

在编码负数时,signed int类型(sint32sint64)与"standard"int类型(int32int64)之间存在重要差异.如果你使用int32int64作为负数的类型,结果varint总是十个字节长 - 它实际上被视为一个非常大的无符号整数.如果您使用其中一种签名类型,则结果varint使用ZigZag编码,这样效率更高.

ZigZag编码将有符号整数映射到无符号整数,因此具有较小绝对值(例如-1)的数字也具有较小的varint编码值.它通过正负整数来回"zig-zags"的方式做到这一点,因此-1被编码为1,1被编码为2,-2被编码为3,依此类推......

因此,即使您使用负数很少,只要您在协议中传递的数字(包括非负数)的幅度较小,您可能最好使用sint32.如果您不确定,那么分析将是有序的.


jsc*_*410 5

很少有充分的理由使用 int* 而不是 sint*。这些额外类型的存在很可能是出于历史、向后兼容性的原因,Protocol Buffers 甚至试图在其自己的协议版本之间维护。

我最好的猜测是,在最早的版本中,他们在 2 的补码表示中愚蠢地编码了负整数,这需要 9 个字节的最大大小的 varint 编码(不包括额外的类型字节)。然后他们坚持使用该编码,以免破坏已经使用它的旧代码和序列化。因此,他们需要添加一种新的编码类型 sint*,以便在不破坏现有代码的情况下为负数获得更好的可变大小编码。设计师如何从一开始就没有意识到这个问题,这完全超出了我的理解。

varint 编码(无类型说明,需要 1 个多字节)可以将无符号整数值编码为以下字节数:

[0, 2^7): 一个字节

[2^7, 2^14):两个字节

[2^14, 2^21):三个字节

[2^21, 2^28):四个字节

[2^28, 2^35):五个字节

[2^35, 2^42):六个字节

[2^42, 2^49):七个字节

[2^49, 2^56):8 个字节

[2^56, 2^64):九个字节

如果你想对小幅度的负整数进行类似的紧凑编码,那么你需要“用完”一位来表示符号。您可以通过显式符号位(在某个保留位置)和幅度表示来做到这一点。或者,您可以进行 zig zag 编码,通过将幅度左移 1 位并为负数减去 1(因此最低有效位表示符号:偶数为非负,几率为负)来有效地执行相同的操作。

无论哪种方式,整数需要更多空间的切入点现在提前了 2 倍:

[0, 2^6): 一个字节

[2^6, 2^13):两个字节

[2^13, 2^20):三个字节

[2^20, 2^27):四个字节

[2^27, 2^34):五个字节

[2^34, 2^41):六个字节

[2^41, 2^48):七个字节

[2^48, 2^55):8 个字节

[2^55, 2^63):九个字节

为了说明在 sint* 上使用 int*,负数必须非常少见,但也是可能的,和/或您希望编码的最常见的正值必须恰好落在引导的切入点之一附近到 sint* 中更大的编码,而不是 int*(例如 - 2^6 vs. 2^7 导致 2x 编码大小)。

基本上,如果您要使用一些可能为负的数字,那么默认情况下使用 sint* 而不是 int*。int* 很少会更优秀,通常甚至不值得你必须致力于判断它是否值得,恕我直言。

  • 并了解您的“[ )”符号:/sf/answers/307741241/ (2认同)