Protocol buffers 在Google协议缓冲区中使用int32而不是sint32是否合适?

Protocol buffers 在Google协议缓冲区中使用int32而不是sint32是否合适?,protocol-buffers,primitive,Protocol Buffers,Primitive,我最近一直在阅读,它允许在消息中使用各种标量值类型 根据,有三种类型的可变长度整数原语-int32、uint32和sint32。在他们的文档中,他们指出,int32编码负数是“效率低下的–如果您的字段可能有负值,请改用sint32。”但是如果您的字段没有负数,我认为uint32无论如何都比int32更适合使用(由于额外的位和减少了处理负数的CPU成本) 那么什么时候使用int32是一个好的标量呢?文档是否暗示只有在很少得到负数的情况下它才是最有效的?或者根据字段的内容,总是最好使用sint32和

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

根据,有三种类型的可变长度整数原语-
int32
uint32
sint32
。在他们的文档中,他们指出,
int32
编码负数是“效率低下的–如果您的字段可能有负值,请改用
sint32
。”但是如果您的字段没有负数,我认为uint32无论如何都比
int32
更适合使用(由于额外的位和减少了处理负数的CPU成本)

那么什么时候使用
int32
是一个好的标量呢?文档是否暗示只有在很少得到负数的情况下它才是最有效的?或者根据字段的内容,总是最好使用
sint32
uint32


(同样的问题也适用于这些标量的64位版本:
int64
uint64
、和
sint64
;但为了可读性,我在问题描述中遗漏了它们。)

我不熟悉谷歌协议缓冲区,但我对文档的解释是:

  • 如果值不能为负值,则使用
    uint32
  • 如果值很可能是负值,则使用
    sint32
    (对于“as-likely be”的模糊定义)
  • 如果值可能为负值,但可能性远小于正值,则使用
    int32
    (例如,如果应用程序有时使用-1表示错误或“未知”值,这是一种相对不常见的情况)
以下是文档对编码()的看法:

有符号整数类型(
sint32
sint64
)与“标准”整数类型(
int32
int64
)之间存在着重要的区别在编码负数时。如果使用
int32
int64
作为负数的类型,则生成的
varint
总是10字节长–实际上,它被视为一个非常大的无符号整数。如果使用其中一种有符号类型,则生成的
varint
使用之字形编码,这样效率更高

锯齿形编码将有符号整数映射为无符号整数,因此具有较小绝对值(例如,-1)的数字也具有较小的
varint
编码值。它以“锯齿形”的方式实现通过正整数和负整数来回转换,-1被编码为1,1被编码为2,-2被编码为3,依此类推


所以看起来,即使你很少使用负数,只要数字的大小(包括非负数)如果您传入的协议较小,则最好使用
sint32
。如果您不确定,则可以进行分析。

没有什么好的理由使用int*而不是sint*。这些额外类型的存在最有可能是出于历史、向后兼容的原因,而这正是协议所需要的rs甚至试图在其自己的协议版本中维护

我最好的猜测是,在最早的版本中,他们在2的补码表示中对负整数进行了无声编码,这要求最大大小的变量编码为9字节(不包括额外的类型字节)。然后他们坚持使用这种编码,以免破坏旧代码和已经使用它的序列化。因此,他们需要添加一种新的编码类型,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):八个字节

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

如果你想对小的负整数进行类似的压缩编码,那么你需要“用尽”一位来表示符号。你可以通过一个显式符号位(在某个保留位置)来实现这一点或者,你可以做Z字形编码,它有效地做同样的事情,将幅度左移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):八个字节

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

若要在sint*上使用int*,负数必须非常罕见,但也有可能,和/或您希望编码的最常见的正值必须正好落在一个转换点附近,该转换点导致sint*中的编码比int*大(例如-2^6与2^7导致2x编码大小)


基本上,如果你的数字中有一些可能是负数,那么默认情况下使用sint*而不是int*。int*很少优于int*,通常甚至不值得你花费额外的精力来判断它是否值得,IMHO。

并理解你的
[)
符号: