无符号与有符号整数的性能
通过使用无符号整数而不是有符号整数有什么性能增益/损失?
如果是这样,这是否也是短期和长期?
2的幂乘除法运算速度更快,因为它可以优化成单个移位指令。 使用带signed int
,通常需要更多的机器指令,因为除法舍入为零 ,但向右舍入向下舍入 。 例:
int foo(int x, unsigned y) { x /= 8; y /= 8; return x + y; }
以下是相关的x
部分(有符号的部分):
movl 8(%ebp), %eax leal 7(%eax), %edx testl %eax, %eax cmovs %edx, %eax sarl $3, %eax
这里是相关的y
部分(未签名的部分):
movl 12(%ebp), %edx shrl $3, %edx
在C ++(和C)中,有符号整数溢出未定义,而无符号整数溢出被定义为环绕。 请注意,例如在gcc中,可以使用-fwrapv标志来定义有符号溢出(以环绕)。
未定义的有符号整数溢出允许编译器假定溢出不会发生,这可能会引入优化机会。 参见例如这个博客文章的讨论。
这将取决于具体的实施。 在大多数情况下,没有区别。 如果你真的在意你必须尝试你考虑的所有变种,并衡量性能。
unsigned
导致与unsigned
相同或更好的性能。 一些例子:
- 按照2的幂来划分常量(另请参阅FredOverflow的答案)
- 由一个常数分区(例如,我的编译器实现除以13使用2个asm指令为无符号,6个指令为签名)
- 检查一个数字是否是偶数(我不知道为什么我的MS Visual Studio编译器用4个有
signed
数的指令来实现它; gcc用1条指令来处理它,就像在unsigned
情况下一样)
short
通常会导致与int
相同或更差的性能(假设sizeof(short) < sizeof(int)
)。 当您将一个算术运算的结果(通常是int
,从不short
)赋给一个存储在处理器的寄存器(也是int
types)的short
types的variables时,会发生性能下降。 从short
到int
所有转换都需要时间,而且很烦人。
注意:有些DSP对于有signed short
型有快速乘法指令; 在这个特定情况下, short
比int
快。
至于int
和long
之间的区别,我只能猜测(我不熟悉64位体系结构)。 当然,如果int
和long
有相同的大小(在32位平台上),它们的性能也是一样的。
一个非常重要的补充,由几个人指出:
对于大多数应用来说真正重要的是内存占用和使用带宽。 对于大型数组,应该使用最小的必要整数( short
,甚至可能是signed/unsigned char
)。
这样可以获得更好的性能,但增益是非线性的(即不是2或4的倍数),并且有点不可预知 – 它取决于caching大小以及应用程序中计算和内存传输之间的关系。
这非常依赖于特定的处理器。
在大多数处理器上,都有有符号和无符号算术的指令,所以使用有符号和无符号整数的区别在于编译器使用哪一个。
如果两者中的任何一个更快,那么它完全是处理器的特定,如果它完全存在的话,它们之间的差别很可能是微乎其微的。
有符号和无符号整数之间的性能差异实际上比接受答案提供的更一般。 无论常数是2的幂,无论常数的无符号整数除以有符号整数除以常量,都可以更快。 请参阅http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html
在他的post结尾,他包括以下部分:
一个自然的问题是,相同的优化是否可以改善有符号的划分 不幸的是,这似乎不是,有两个原因:
股息的增量必须增加幅度,即如果n> 0,则递增,如果n <0则递减。这引入额外的费用。
对不合作的除数的惩罚只有签名师的一半左右,留下一个小改进的窗口。
因此,似乎可以使得舍入algorithm在有符号划分中工作,但是将不如标准舍入algorithm。
总之,不要打扰事实。 但是,之后不要麻烦。
如果你想获得性能,你必须使用可能违背常识的编译器性能优化 。 有一点要记住,不同的编译器可以不同的编译代码,它们本身也有不同的优化。 如果我们正在讨论一个g++
编译器,并且使用-Ofast
或至less一个-O3
标志来-Ofast
它的优化级别,那么根据我的经验,它可以将long
types编译成比任何unsigned
types更好的代码,或者即使只是int
。
这是从我自己的经验,我build议你先写完整的程序,并关心这些事情之后,当你有你的手中的实际代码,你可以编译它与优化试图挑选types,实际执行最好。 这也是关于性能代码优化的一个非常普遍的build议,首先快速编写,尝试优化编译,调整一些东西,看看什么效果最好。 你也应该尝试使用不同的编译器来编译你的程序,并select输出性能最高的机器码。
优化的multithreading代数计算程序可以很容易地将性能差异> 10倍,而不是优化的。 所以这很重要。
在很多情况下,优化器输出与逻辑相矛盾。 例如,我有一个情况,当a[x]+=b
和a[x]=b
之间的差异改变了程序执行时间几乎2倍。 不, a[x]=b
不是更快的那个。
例如, NVidia指出 ,要编程他们的GPU:
注意:正如已经推荐的最佳做法,只要有可能,在SMM上获得最佳吞吐量时,有符号算术应优先于无符号算术。 C语言标准对无符号math的溢出行为设置了更多的限制,限制了编译器优化的机会。
传统上, int
是目标硬件平台的本地整数格式。 任何其他整数types可能会导致性能处罚。
编辑:
现代系统的情况略有不同:
-
由于兼容性的原因,
int
可能实际上是64位系统上的32位。 我相信这发生在Windows系统上。 -
在某些情况下,现代编译器可能会在执行较短types的计算时隐式使用
int
。
IIRC,在x86签名/无签名应该没有任何区别。 另一方面,短/长是一个不同的故事,因为需要从RAM中移出的数据量较大(其他原因可能包括从短到长的投射操作)。
无符号整数是有利的,因为你存储和处理两个比特stream,我的意思是一个数据,没有符号,所以乘法,devision变得更容易(更快)的位移操作
2的幂除以无符号types更快,其他值除以无符号types也更快。 如果您查看Agner Fog的指令表,您会发现未签名的部门与已签署的版本具有相似或更好的性能
例如与AMD K7
╔═════════════╤══════════╤═════╤═════════╤═══════════════════════╗ ║ Instruction │ Operands │ Ops │ Latency │ Reciprocal throughput ║ ╠═════════════╪══════════╪═════╪═════════╪═══════════════════════╣ ║ DIV │ r8/m8 │ 32 │ 24 │ 23 ║ ║ DIV │ r16/m16 │ 47 │ 24 │ 23 ║ ║ DIV │ r32/m32 │ 79 │ 40 │ 40 ║ ║ IDIV │ r8 │ 41 │ 17 │ 17 ║ ║ IDIV │ r16 │ 56 │ 25 │ 25 ║ ║ IDIV │ r32 │ 88 │ 41 │ 41 ║ ║ IDIV │ m8 │ 42 │ 17 │ 17 ║ ║ IDIV │ m16 │ 57 │ 25 │ 25 ║ ║ IDIV │ m32 │ 89 │ 41 │ 41 ║ ╚═════════════╧══════════╧═════╧═════════╧═══════════════════════╝
同样的事情适用于英特尔奔腾
╔═════════════╤══════════╤══════════════╗ ║ Instruction │ Operands │ Clock cycles ║ ╠═════════════╪══════════╪══════════════╣ ║ DIV │ r8/m8 │ 17 ║ ║ DIV │ r16/m16 │ 25 ║ ║ DIV │ r32/m32 │ 41 ║ ║ IDIV │ r8/m8 │ 22 ║ ║ IDIV │ r16/m16 │ 30 ║ ║ IDIV │ r32/m32 │ 46 ║ ╚═════════════╧══════════╧══════════════╝
当然那些是相当古老的。 具有更多晶体pipe的较新架构可能会缩小差距,但是基本的东西是适用的:一般来说,您需要更多的macros操作,更多的逻辑,更多的延迟来执行有符号的划分