对于从不期望采用 -ve 值的整数,可以是 unsigned int 或 int。从编译器的角度或纯 cpu 周期的角度来看, x86_64 有什么区别吗?
5 回答
这取决于。它可能会采取任何一种方式,具体取决于您正在做什么int
以及底层硬件的属性。
一个明显的例子unsigned int
是整数除法运算。在 C/C++ 中,整数除法应该朝零舍入,而 x86 上的机器整数除法则朝负无穷大舍入。此外,整数除法(移位等)的各种“优化”替换通常也向负无穷大舍入。因此,为了满足标准要求,编译器被迫使用额外的机器指令调整有符号整数除法结果。在无符号整数除法的情况下,不会出现此问题,这就是为什么整数除法对于无符号类型的工作通常比有符号类型快得多的原因。
例如,考虑这个简单的表达式
rand() / 2
MSVC 编译器为此表达式生成的代码通常如下所示
call rand
cdq
sub eax,edx
sar eax,1
请注意,我们在这里看到的不是单个移位指令 ( sar
),而是一大堆指令,即我们sar
的前面有两个额外的指令 (cdq
和sub
)。这些额外的指令只是为了“调整”除法以强制它生成“正确的”(从 C 语言的角度来看)结果。请注意,编译器不知道您的值将始终为正,因此它必须始终无条件地生成这些指令。他们永远不会做任何有用的事情,从而浪费 CPU 周期。
不看代码
(unsigned) rand() / 2
这只是
call rand
shr eax,1
在这种情况下,一个单一的转变就可以了,从而为我们提供了一个天文数字更快的代码(仅用于除法)。
另一方面,当您混合整数算术和 FPU 浮点算术时,有符号整数类型可能工作得更快,因为 FPU 指令集包含用于加载/存储有符号整数值的立即指令,但没有用于无符号整数值的指令。
为了说明这一点,可以使用以下简单函数
double zero() { return rand(); }
生成的代码一般会很简单
call rand
mov dword ptr [esp],eax
fild dword ptr [esp]
但是,如果我们将函数更改为
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
所做的)。
还有其他上下文和示例可用于证明它以任何一种方式工作。所以,再一次,这一切都取决于。但一般来说,所有这些在您的程序性能的大局中都无关紧要。我通常更喜欢使用无符号类型来表示无符号数量。在我的代码中,99% 的整数类型都是无符号的。但我这样做纯粹是出于概念上的原因,而不是为了任何性能提升。
在大多数情况下,有符号类型本质上更易于优化,因为编译器可以忽略溢出的可能性,并以它认为合适的任何方式简化/重新排列算术。另一方面,无符号类型本质上更安全,因为结果总是定义明确的(即使不是你天真的认为的那样)。
无符号类型更好优化的一种情况是当您编写除法/余数乘以 2 的幂时。对于无符号类型,这直接转换为位移位和位与。对于有符号类型,除非编译器可以确定该值已知为正,否则它必须生成额外的代码来补偿负数的非一问题(根据 C,-3/2 是 -1,而代数和按位运算是-2)。
几乎可以肯定没有什么区别,但有时编译器可以使用类型的符号来玩游戏以减少几个周期,但老实说,总体而言这可能是一个微不足道的变化。
例如假设你有一个int x
并且想要写:
if(x >= 10 && x < 200) { /* ... */ }
您(或者更好的是,编译器)可以稍微改变一下以少做一次比较:
if((unsigned int)(x - 10) < 190) { /* ... */ }
这是一个以 2 的补码表示的假设int
,因此当(x - 10)
被视为. 例如,在一个典型的 x86 系统上,它显然比被测试的要大。0
unsigned int
(unsigned int)-1 == 0xffffffff
190
这充其量是微优化,最好留给编译器,相反,您应该编写表达您的意思的代码,如果它太慢,请分析并确定真正需要变得聪明的地方。
我不认为它在 CPU 或编译器方面会有很大的不同。一种可能的情况是,如果它使编译器知道该数字永远不会是负数并优化掉代码。
但是,它对阅读您的代码的人很有用,因此他们知道相关变量的域。
从 ALU 的角度来看,添加(或其他)有符号或无符号值没有任何区别,因为它们都由一组位表示。0100 + 1011
总是1111
,但您选择是4 + (-5) = -1
还是4 + 11 = 15
。
所以我同意@Mark,你应该选择最好的数据类型来帮助别人理解你的代码。