标记int unsigned的编译器优化?

对于一个从不期望采用-ve值的整数,可以使用unsigned int或int。 从编译器角度或纯cpu周期的角度来看,x86_64有什么区别吗?

这取决于。 它可能采用任何一种方式,具体取决于您使用该int以及底层硬件的属性。


unsigned int s的一个明显例子是整数除法运算。 在C / C ++中,整数除法应该向零舍入 ,而x86上的机器整数除法向负无穷大舍入 。 此外,用于整数除法(移位等)的各种“优化的”替换也通常向负无穷大舍入。 因此,为了满足标准要求,编译器必须使用附加的机器指令调整带符号的整数除法结果。 在无符号整数除法的情况下,不会出现这个问题,这就是为什么通常整数除法对于无符号类型比对有符号类型更快。

例如,考虑这个简单的表达式

 rand() / 2 

MSVC编译器为此表达式生成的代码通常如下所示

 call rand cdq sub eax,edx sar eax,1 

请注意,我们在这里看到一大堆指令,而不是单个移位指令( sar ),即我们的sar前面有两个额外的指令( cdqsub )。 这些额外的指令只是为了“调整”除法,以迫使它生成“正确的”(从C语言的角度来看)结果。 请注意,编译器不知道您的值将始终为正,因此必须始终无条件地生成这些指令。 他们永远不会做任何有用的事情,从而浪费CPU周期。

不看看代码

 (unsigned) rand() / 2 

它只是

 call rand shr eax,1 

在这种情况下,单个class次就可以完成这一操作,从而为我们提供了一个天文数字更快的代码(仅用于划分)。


另一方面,当您混合整数算术和FPU浮点算术时,有符号整数类型可能更快,因为FPU指令集包含加载/存储有符号整数值的立即指令,但没有无符号整数值的指令。

为了说明这一点,可以使用以下简单的function

 double zero() { return rand(); } 

生成的代码通常非常简单

 call rand mov dword ptr [esp],eax fild dword ptr [esp] 

但是,如果我们改变我们的function

 double zero() { return (unsigned) rand(); } 

生成的代码将更改为

 call rand test eax,eax mov dword ptr [esp],eax fild dword ptr [esp] jge zero+17h fadd qword ptr [__real@41f0000000000000 (4020F8h)] 

此代码明显更大,因为FPU指令集不能与无符号整数类型一起使用,因此在加载无符号值(这是条件fadd所做的)之后需要进行额外的调整。


还有其他上下文和示例可用于certificate它以任何一种方式工作。 所以,这一切都取决于。 但一般来说,所有这些对于你的程序性能的大局而言并不重要。 我通常更喜欢使用无符号类型来表示无符号数量。 在我的代码中,99%的整数类型是无符号的。 但我这样做纯粹是出于概念上的原因,而不是任何性能提升。

在大多数情况下,有符号类型本质上更可优化,因为编译器可以忽略溢出的可能性,并以它认为合适的方式简化/重新排列算术。 另一方面,无符号类型本质上更安全,因为结果总是很明确(即使不是你天真以为它应该是什么)。

无符号类型可以更好地优化的一种情况是当你用2的幂写入除法/余数时。 对于无符号类型,这直接转换为bitshift和bitwise。 对于有符号类型,除非编译器可以确定该值已知为正数,否则它必须生成额外的代码以补偿带负数的逐个问题(根据C,-3 / 2为-1,而代数和按位运算,它是-2)。

它几乎肯定没有区别,但偶尔编译器可以使用类型的签名来玩游戏以便削减几个周期,但说实话,它总体上可能是一个微不足道的变化。

例如,假设您有一个int x并且想要写:

 if(x >= 10 && x < 200) { /* ... */ } 

您(或者更好的是,编译器)可以将此转换为少做一个比较:

 if((unsigned int)(x - 10) < 190) { /* ... */ } 

这假设int用2的恭维表示,因此如果(x - 10)小于0则当被视为unsigned int时, 0变为巨大的值。 例如,在典型的x86系统上, (unsigned int)-1 == 0xffffffff ,这明显大于正在测试的190

这是最好的微优化,最好留下编译器,而不是你应该编写表达你的意思的代码,如果它太慢,分析并决定在哪里真正有必要变得聪明。

我不认为它会在CPU或编译器方面产生太大的影响。 一种可能的情况是,如果它使编译器知道该数字永远不会是负数并优化掉代码。

但是,对于阅读代码的人来说,它是有用的,因此他们知道变量的域。

从ALU的角度来看,添加(或者其他)有符号或无符号值没有任何区别,因为它们都由一组位表示。 0100 + 1011始终为1111 ,但选择是4 + (-5) = -1还是4 + 11 = 15
所以我同意@Mark,你应该选择最好的数据类型来帮助其他人理解你的代码。