无符号与有符号整数的性能

Fle*_*exo 67 c c++ int unsigned integer

通过在有符号整数上使用无符号整数,是否有任何性能增益/损失?

如果是这样,这也是短期和长期的吗?

fre*_*low 100

2的幂除法更快unsigned int,因为它可以优化为单个移位指令.有了signed int它,它通常需要更多的机器指令,因为除法向零舍入,但向右舍入向下舍入.例:

int foo(int x, unsigned y)
{
    x /= 8;
    y /= 8;
    return x + y;
}
Run Code Online (Sandbox Code Playgroud)

这是相关x部分(签名部门):

movl 8(%ebp), %eax
leal 7(%eax), %edx
testl %eax, %eax
cmovs %edx, %eax
sarl $3, %eax
Run Code Online (Sandbox Code Playgroud)

以下是相关y部分(未签名部门):

movl 12(%ebp), %edx
shrl $3, %edx
Run Code Online (Sandbox Code Playgroud)

  • 这只有在除数是一个已知的常数已知常数为2的幂时才起作用,不是吗? (10认同)
  • 在这种规模上,对于现代流水线 CPU 架构来说,“更多指令”并不总是意味着“运行时间更慢”。也就是说,在得出影响深远的结论之前,我仍然会先进行测量。 (2认同)

kbj*_*klu 46

在C++(和C)中,有符号整数溢出是未定义的,而无符号整数溢出被定义为环绕.请注意,例如在gcc中,您可以使用-fwrapv标志来定义已签名的溢出(以换行).

未定义的有符号整数溢出允许编译器假定不会发生溢出,这可能会引入优化机会.请参阅此博客文章以供讨论.

  • 请注意,自 C++ 20 以来,情况并非如此。 (4认同)

ana*_*lyg 18

unsigned导致相同或更好的表现signed.一些例子:

  • 除以2的幂的常数除法(参见FredOverflow的答案)
  • 除以常数(例如,我的编译器使用2个asm指令实现除以13的除法,以及6个用于签名的指令)
  • 检查一个数字是否是偶数(我不知道为什么我的MS Visual Studio编译器使用4个signed数字指令实现它; gcc使用1个指令执行它,就像在这种unsigned情况下一样)

short通常导致与int(假设sizeof(short) < sizeof(int))相同或更差的表现.当您将算术运算的结果(通常是int永远的short)分配给类型的变量时会发生性能下降,该变量short存储在处理器的寄存器(也是类型int)中.所有转换shortint需要时间并且很烦人.

注意:某些DSP具有signed short类型的快速乘法指令; 在这种特定情况下short比...更快int.

至于int和之间的区别long,我只能猜测(我不熟悉64位架构).当然,如果int并且long具有相同的大小(在32位平台上),它们的性能也是相同的.


一些非常重要的补充,几个人指出:

对大多数应用程序而言真正重要的是内存占用和带宽利用.对于大型数组short,您应该使用最小的必要整数(可能是偶数signed/unsigned char).

这将提供更好的性能,但增益是非线性的(即不是2或4倍)并且有些不可预测 - 它取决于缓存大小以及应用程序中计算和内存传输之间的关系.

  • 关于short的性能与int相比,我会小心.虽然算术"可能"使用int更快,但应该记住整数算术很少是瓶颈(至少在现代桌面cpus上),另一方面通常是内存带宽,因此对于大型数据集而言,short可能实际上提供了相当好的性能INT.此外,对于使用较小数据类型的自动向量化代码,通常意味着可以在一个数据元素中执行更多数据元素,因此即使算术性能可能会增加(尽管在自动向量化器的当前状态下不太可能). (7认同)
  • @JoshParnell我猜你的意思是当*内存限制*时,“short”比“int”更快。根据我的经验,它们在 x86 上具有相同的性能,而“short”在 ARM 上速度较慢。 (3认同)
  • @martinkunev绝对!这可能是今天使用"short"的唯一原因(非缓存RAM实际上是无限的),这是一个很好的理由. (2认同)

sha*_*oth 16

这将取决于具体实施.但在大多数情况下,没有区别.如果您真的在意,您必须尝试所有您考虑的变体并测量性能.

  • `+ 1`表示"如果你想知道,你需要测量".这需要几乎每周回答,这非常烦人. (20认同)

Seb*_*olm 9

这几乎取决于特定的处理器.

在大多数处理器上,都有有符号和无符号算术的指令,因此使用有符号和无符号整数之间的区别归结为编译器使用的整数.

如果两者中的任何一个更快,那么它完全取决于处理器,如果它存在的话,很可能差别很小.


Dav*_*one 6

有符号和无符号整数之间的性能差异实际上比接受答案所暗示的更为普遍.任何常数对无符号整数的划分都可以比有符号整数除以常数更快,无论常数是2的幂.请参见http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html

在他的帖子结束时,他包括以下部分:

一个自然的问题是相同的优化是否可以改善签名划分; 不幸的是,它似乎没有,原因有两个:

被除数的增量必须变为幅度的增加,即如果n> 0则增加,如果n <0则递减.这引入了额外的费用.

不合作除数的惩罚只是签名师的一半左右,留下了较小的改进窗口.

因此,似乎可以使得向下舍入算法在有符号的除法中工作,但是将不如标准的向上舍入算法.


phu*_*clv 5

使用无符号类型不仅除以 2 的幂更快,而且使用无符号类型除以任何其他值也更快。如果您查看Agner Fog 的指令表,您会发现未签名的除法与签名版本具有相似或更好的性能

以 AMD K7 为例

操作说明 操作数 行动 潜伏 吞吐量倒数
DIV r8/m8 32 24 23
DIV r16/m16 47 24 23
DIV r32/m32 79 40 40
免疫缺陷病毒 r8 41 17 号 17 号
免疫缺陷病毒 r16 56 25 25
免疫缺陷病毒 r32 88 41 41
免疫缺陷病毒 米8 42 17 号 17 号
免疫缺陷病毒 米16 57 25 25
免疫缺陷病毒 米32 89 41 41

同样的情况也适用于英特尔奔腾

操作说明 操作数 时钟周期
DIV r8/m8 17 号
DIV r16/m16 25
DIV r32/m32 41
免疫缺陷病毒 r8/m8 22
免疫缺陷病毒 r16/m16 30
免疫缺陷病毒 r32/m32 46

当然,这些都是相当古老的。具有更多晶体管的新型架构可能会缩小差距,但基本的事情是适用的:通常您需要更多的微操作、更多的逻辑、更多的芯片面积、更多的延迟来进行有符号除法