对于一个永远不会取 -ve 值的整数,可以是 unsigned int 或 int。 从编译器的角度或纯粹的 cpu 周期角度来看,x86_64 有什么区别吗?
最佳答案
这取决于。它可能会采用任何一种方式,具体取决于您对该 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 指令集包含用于加载/存储有符号整数值的立即指令,但没有用于无符号整数的指令值(value)观。
为了说明这一点可以使用下面的简单函数
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% 的整数类型都是无符号的。但我这样做纯粹是出于概念上的原因,而不是为了提高性能。
关于c++ - 标记 int unsigned 的编译器优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4889707/