通过在有符号整数上使用无符号整数是否有任何性能增益/损失?
如果是这样,这种情况会持续多长时间?
答案 0 :(得分:93)
使用unsigned int
分割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
答案 1 :(得分:41)
在C ++(和C)中,有符号整数溢出是未定义的,而无符号整数溢出被定义为环绕。请注意,例如在gcc中,您可以使用-fwrapv标志来定义已签名的溢出(以换行)。
未定义的有符号整数溢出允许编译器假设不会发生溢出,这可能会引入优化机会。参见例如this blog post进行讨论。
答案 2 :(得分:17)
这取决于具体实施。但在大多数情况下,没有区别。如果你真的在乎你必须尝试你考虑的所有变体并测量性能。
答案 3 :(得分:16)
unsigned
导致与signed
相同或更好的表现。
一些例子:
signed
数字指令实现它; gcc使用1条指令执行它,就像在unsigned
情况下一样) short
通常会导致与int
相同或更差的表现(假设sizeof(short) < sizeof(int)
)。将算术运算的结果(通常为int
,从不short
)分配给类型为short
的变量时会发生性能下降,该变量存储在处理器的寄存器中(即也是类型int
)。从short
到int
的所有转化都需要时间并且很烦人。
注意:某些DSP具有signed short
类型的快速乘法指令;在此特定情况下,short
比int
快。
至于int
和long
之间的区别,我只能猜测(我不熟悉64位架构)。当然,如果int
和long
具有相同的大小(在32位平台上),它们的性能也是相同的。
一些非常重要的补充,有几个人指出:
对大多数应用程序而言真正重要的是内存占用和带宽利用。对于大型数组,您应该使用最小的必要整数(short
,甚至可能signed/unsigned char
)。
这将提供更好的性能,但增益是非线性的(即不是2或4倍)并且有些不可预测 - 它取决于缓存大小以及应用程序中计算和内存传输之间的关系。
答案 4 :(得分:8)
这几乎取决于具体的处理器。
在大多数处理器上,都有有符号和无符号算术的指令,因此使用有符号和无符号整数之间的区别取决于编译器使用哪一个。
如果两者中的任何一个更快,那么它完全取决于处理器,如果它存在的话,很可能差别很小。
答案 5 :(得分:6)
有符号和无符号整数之间的性能差异实际上比接受答案所暗示的更为普遍。任何常数对无符号整数的划分都可以比有符号整数除以常数更快,无论常数是2的幂。见http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html
在他的帖子结束时,他包括以下部分:
一个自然的问题是,相同的优化是否可以改善签署的划分;不幸的是,它似乎没有,原因有两个:
被除数的增量必须变为幅度的增加,即如果n> 1则增加。如果n&lt; 0,则递减这引入了额外的费用。
对于不合作的除数,惩罚只是签约师的一半,留下了较小的改进窗口。
因此,似乎可以使得向下舍入算法在有符号的除法中工作,但是将落后于标准的向上舍入算法。
答案 6 :(得分:3)
简而言之,在事实之前不要打扰。但是请在此之后烦恼。
如果您希望获得性能,必须使用编译器的性能优化,这可能会违反常识。要记住的一件事是,不同的编译器可以以不同的方式编译代码,并且它们本身具有不同类型的优化。如果我们正在讨论g++
编译器并讨论通过使用-Ofast
或至少-O3
标志来最大化它的优化级别,根据我的经验,它可以编译{{1}输入代码的性能甚至超过任何long
类型,甚至只是unsigned
。
这是我自己的经验,我建议你先编写完整的程序,然后关心这些事情,当你掌握了实际的代码并且可以通过优化编译它来尝试选择类型时实际上表现最好。这也是关于性能代码优化的一般性建议,首先快速编写,尝试使用优化进行编译,调整内容以查看最佳方法。您还应该尝试使用不同的编译器来编译程序并选择输出性能最佳的机器代码。
优化的多线程线性代数计算程序可以轻松地对未经优化的优化进行精细优化,具有> 10倍的性能差异。所以这很重要。
在很多情况下,优化器输出与逻辑相矛盾。例如,我有一个案例,int
和a[x]+=b
之间的差异改变了程序执行时间几乎2倍。不,a[x]=b
不是更快的。
以下是用于编程GPU的示例NVidia stating:
注意:已经是推荐的最佳实践,签名算术 应尽可能优先于无符号算术 SMM的最佳吞吐量。 C语言标准更多 对无符号数学的溢出行为的限制,限制编译器 优化机会。
答案 7 :(得分:3)
对于无符号类型,不仅除以2的幂更快,对于无符号类型,除以任何其他值也更快。如果您查看Agner Fog's Instruction tables,您会发现无符号分部的性能与签名版本相似或更好
例如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 ║
╚═════════════╧══════════╧══════════════╝
当然那些都很古老。具有更多晶体管的较新架构可能缩小差距但基本的事情适用:通常您需要更多的宏操作,更多逻辑,更多延迟来执行签名划分
答案 8 :(得分:0)
传统上int
是目标硬件平台的本机整数格式。任何其他整数类型都可能导致性能损失。
编辑:
现代系统的情况略有不同:
int
实际上可能是64位系统上的32位。我相信这会发生在Windows系统上。
在某些情况下,对较短类型执行计算时,现代编译器可能会隐式使用int
。
答案 9 :(得分:0)
答案 10 :(得分:0)
无符号整数是有利的,因为你将它们存储和处理为比特流,我的意思是只是一个没有符号的数据,所以乘法,使用位移操作变得更容易(更快)
答案 11 :(得分:0)
有符号和无符号整数将始终作为单个时钟指令运行并具有相同的读写性能,但根据Dr Andrei Alexandrescu无符号优先于有符号。这样做的原因是你可以在相同的位数中输入两倍的数字,因为你没有浪费符号位,你将使用较少的指令检查负数,从而减少ROM的性能提升。根据我对使用超高性能Kabuki VM实现的Script的体验,在使用内存时实际上很少需要签名号码。我花了几年的时间用有符号和无符号数进行指针运算,并且在没有符号位的情况下我发现签名没有任何好处。
当签名可能是首选时,使用位移来执行乘法和2的幂除法,因为你可以用带符号2的补码整数执行2除法的负幂。有关更多优化技巧,请参阅一些more YouTube videos from Andrei。您还可以在我的文章中找到有关the world's fastest Integer-to-String conversion algorithm。
的一些好消息