考虑下一个代码:
unsigned idx;
//.. some work with idx
if( idx >= idx_max )
idx %= idx_max;
可以简化为仅第二行:
idx %= idx_max;
并且将达到相同的结果。
我多次遇到下一个代码:
unsigned x;
//... some work with x
if( x!=0 )
x=0;
可以简化为
x=0;
问题:
- 使用
if
有什么意义吗?为什么?尤其是 ARM Thumb 指令集。 - 可以省略这些
if
吗? - 编译器做了哪些优化?
最佳答案
如果您想了解编译器在做什么,您只需提取一些程序集即可。我推荐这个网站(我已经从问题中输入了代码)):https://godbolt.org/g/FwZZOb .
第一个例子更有趣。
int div(unsigned int num, unsigned int num2) {
if( num >= num2 ) return num % num2;
return num;
}
int div2(unsigned int num, unsigned int num2) {
return num % num2;
}
生成:
div(unsigned int, unsigned int): # @div(unsigned int, unsigned int)
mov eax, edi
cmp eax, esi
jb .LBB0_2
xor edx, edx
div esi
mov eax, edx
.LBB0_2:
ret
div2(unsigned int, unsigned int): # @div2(unsigned int, unsigned int)
xor edx, edx
mov eax, edi
div esi
mov eax, edx
ret
基本上,出于非常具体和合乎逻辑的原因,编译器不会优化掉分支。如果整数除法的成本与比较的成本大致相同,那么分支将毫无意义。但是整数除法(通常与模数一起执行)实际上非常昂贵:http://www.agner.org/optimize/instruction_tables.pdf .这些数字因架构和整数大小而有很大差异,但通常可能是 15 到接近 100 个周期的延迟。
通过在执行模数之前取一个分支,实际上可以为自己节省很多工作。但请注意:编译器也不会将没有分支的代码转换为汇编级别的分支。那是因为分支也有一个缺点:如果模数最终还是必要的,那你只是浪费了一点时间。
在不知道 idx < idx_max
的相对频率的情况下,无法对正确的优化做出合理的判断。将是真的。所以编译器(gcc 和 clang 做同样的事情)选择以相对透明的方式映射代码,把这个选择权交给开发者。
所以那个分支可能是一个非常合理的选择。
第二个分支应该是完全没有意义的,因为比较和赋值是的可比成本。也就是说,您可以在链接中看到,如果编译器有对变量的引用,它们仍然不会执行此优化。如果该值是一个局部变量(如在您演示的代码中),那么编译器将优化分支。
总而言之,第一段代码可能是一个合理的优化,第二段可能只是一个累了的程序员。
关于c++ - `if` 语句在取模之前和分配操作之前是多余的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43731203/