标记int unsigned的编译器优化?

Hum*_*ger 1 c c++ low-latency

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

AnT*_*AnT 11

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


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

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

rand() / 2
Run Code Online (Sandbox Code Playgroud)

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

call        rand
cdq              
sub         eax,edx 
sar         eax,1 
Run Code Online (Sandbox Code Playgroud)

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

不看看代码

(unsigned) rand() / 2
Run Code Online (Sandbox Code Playgroud)

它只是

call        rand  
shr         eax,1 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,单个班次完成了这个技巧,从而为我们提供了一个天文学更快的代码(仅用于划分).


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

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

double zero() { return rand(); }
Run Code Online (Sandbox Code Playgroud)

生成的代码通常非常简单

call        rand 
mov         dword ptr [esp],eax 
fild        dword ptr [esp]
Run Code Online (Sandbox Code Playgroud)

但是如果我们改变我们的功能

double zero() { return (unsigned) rand(); }
Run Code Online (Sandbox Code Playgroud)

生成的代码将更改为

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

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


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