c++ - 标记 int unsigned 的编译器优化?

标签 c++ c low-latency

对于一个永远不会取 -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 前面有两条额外的指令 (cdqsub)。这些额外的指令只是为了“调整”除法以强制它生成“正确的”(从 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/

相关文章:

c - 如何从带有 carbon 的插件内部获取包引用?

c# - 学习如何编写延迟关键、快速的 C++/Java/C# 代码的最佳方法?

C++/MFC 如何从 PC 复制多个文件到 WinCE(移动设备)?

c++ - std::string 可以在没有#include <string> 的情况下使用吗?

c - fgets 函数的行为很奇怪

C 代码解析器,用于跟踪项目中的函数调用和变量访问(emacs 兼容性会很好)

c++ - 跨 Windows DLL 模块边界清理堆分配资源的问题

java - jstring转c++字符串结果java代码执行报错

database - 交易应用权衡 : Databases and low-latency

linux - 比较不同机器之间的时间戳