当我偶然发现一个奇怪的问题时,我正在从事一个个人项目。
在一个非常紧密的循环中,我有一个整数,其值在0到15之间。对于值0、1、8和9,我需要获取-1,对于值4、5、12和13,我需要获取1。
我转向godbolt检查一些选项,并惊讶于编译器似乎无法以与if链相同的方式优化switch语句。
链接在这里:https : //godbolt.org/z/WYVBFl
代码是:
const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
int a(int num) {
return lookup[num & 0xF];
}
int b(int num) {
num &= 0xF;
if (num == 0 || num == 1 || num == 8 || num == 9)
return -1;
if (num == 4 || num == 5 || num == 12 || num == 13)
return 1;
return 0;
}
int c(int num) {
num &= 0xF;
switch (num) {
case 0: case 1: case 8: case 9:
return -1;
case 4: case 5: case 12: case 13:
return 1;
default:
return 0;
}
}
我本以为b和c会产生相同的结果,并且我希望我自己可以阅读比特技巧以提出有效的实现,因为我的解决方案(switch语句-另一种形式)相当慢。
奇怪的是,b
编译为位hack的同时c
几乎没有进行优化,或者简化为a
依赖目标硬件的情况。
谁能解释为什么存在这种差异?优化此查询的“正确”方法是什么?
编辑:
澄清度
我希望交换机解决方案是最快的或类似的“干净”解决方案。但是,在我的计算机上进行优化编译时,if解决方案的速度明显更快。
我编写了一个快速程序来演示,并且TIO的结果与我在本地发现的结果相同:在线尝试!
通过static inline
查找表可以加快速度:在线尝试!
if
仍然跳动switch
(奇怪地,查找变得更快)[TIO跟随]
-O3
,并且将其编译c
为可能比a
或更糟糕的东西b
(c
有两次条件跳转加上一些操作,而对于一次条件跳转只进行了更简单的操作b
),但是仍然比幼稚的逐项测试要好。我不确定您在这里真正要的是什么。一个简单的事实是,一个优化的编译器可以将任何这些到任何其他的,如果它选择这样做,有什么会或不会做任何硬性规定。