c++ - “正确”无符号整数比较

标签 c++ c assembly type-conversion micro-optimization

所以,我们都知道 -1 > 2u == true 的 C/C++ 有符号/无符号比较规则,并且我有一种情况,我想有效地实现“正确”比较。

我的问题是,考虑到人们熟悉的尽可能多的架构,哪种方法更有效。显然 Intel 和 ARM 的权重更高。

给定:

int x;
unsigned int y;
if (x < y) {}

推广是不是更好:

x < y  =>  (int64)x < (int64)y

或者执行2次比较更好,即:

x < y  =>  (x < 0 || x < y)

前者意味着零扩展、符号扩展和一个比较+分支,后者不需要符号扩展操作,但需要2个连续的cmp+分支。
传统观点认为分支比符号扩展更昂贵,这两者都会流水线,但在第一种情况下,扩展和单一比较之间存在停顿,而在第二种情况下,我可以想象某些架构可能会流水线化 2 个比较,但随后是2个条件分支?

存在另一种情况,无符号值是比有符号类型更小的类型,这意味着它可以通过对有符号类型的长度进行一次零扩展,然后进行一次比较......在这种情况下,是使用extend+cmp版本更可取,还是2-comparison方法仍然更可取?

英特尔? ARM ?其他的? 我不确定这里是否有正确的答案,但我想听听人们的看法。如今,低级性能很难预测,尤其是在 Intel 上,在 ARM 上也越来越多。

编辑:

我应该补充一下,有一个明显的解决方案,其中类型的大小等于架构的 int 宽度;在这种情况下,显然首选 2 比较解决方案,因为促销本身不能有效地执行。显然,我的 int 示例满足 32 位架构的这个条件,您可以将思想实验转换为 short 以进行应用于 32 位平台的练习。

编辑 2:

对不起,我忘记了 -1 > 2u 中的 u! >_<

编辑 3:

我想修改这种情况,假设比较的结果是一个实际的分支,并且结果不是作为 bool 值返回的。这就是我更喜欢结构外观的方式;尽管这确实提出了一个有趣的观点,即当结果是 bool 值与分支时,还有另一组排列。

int g;
void fun(int x, unsigned in y) { if((long long)x < (long long)y) g = 10; }
void gun(int x, unsigned in y) { if(x < 0 || x < y) g = 10; }

这会产生通常在遇到 if 时隐含的预期分支;)

最佳答案

好吧,您已经正确地描述了这种情况:C/C++ 无法通过单个比较进行完整的有符号整数/无符号整数比较。

如果升级到 int64 比进行两次比较更快,我会感到惊讶。以我的经验,编译器非常善于意识到这样的子表达式是纯粹的(没有副作用),因此不需要第二个分支。 (您也可以使用按位或显式选择退出短路: (x < 0) | (x < y) 。)相比之下,我的经验是编译器往往不会对大于 native 字长的整数进行太多特殊情况优化,因此 (int64)x < (int64)y 很可能实际进行完整的 int 比较。

底线,没有咒语可以保证在任何处理器上产生最好的机器代码,但是对于最常见的处理器上最常见的编译器,我猜两个比较的形式不会比提升到 int64 形式。

编辑:Godbolt 上的一些讨论证实了在 ARM32 上,GCC 在 int64 方法中投入了太多的机器。 VC 在 x86 上做同样的事情。但是,对于 x64,int64 方法实际上缩短了一条指令(因为提升和 64 位比较是微不足道的)。不过,流水线可能会使实际性能变得更糟。 https://godbolt.org/g/wyG4yC

关于c++ - “正确”无符号整数比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44068900/

相关文章:

c - 在 macOS 上使用 Xcode 作为 root 运行应用程序时出现非法指令 (ud2)

c++ - 变量的 const 和 volatile 顺序

c - 这个指针返回函数有问题

C++,在运行时创建类

c - 如何: Safeguard memory - strncat()?

c++ - 如何在excel中使用标准输入 '<'?

assembly - 在实模式下访问 4GB RAM

linux - 汇编语言中的段错误

c++ - Qt:GraphicsView 中的 TextEdit,下方有奇怪的灰色条

类中 vector 的 C++ vector