编译器除法优化
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器除法优化相关的知识,希望对你有一定的参考价值。
我们知道除法在现代CPU计算中耗费更多的时钟周期:
如下图的Add和div做的对比
Div延迟60-80之间 而add只有 1,可见我们在真实的情况应当避免调用除法汇编指令。
解决办法:
对于除以2的倍数优化
对于要进行除法运行的且除数是2的倍数,我们可以通过位移来完成
如下图:
int x = 58;
// x = x%8
x >>= 3;
但是对于负数来说位移是可能发生错误现象的如下:
对于负数我们可以利用如下公式进行转化
举个例子:
a = -58
b = 8
我们本例中结合计算机特性符合如下公式(比如结果是-7.25我们应该向上取而不是向下):
ceil ((a+b-1)/b) =ceil ((-58+8-1)/8) =ceil(-6.375)=-7
于是我们的除法优化可以如此这般消除除法
int x = -58;
//如果被除数小于0 利用公式消除除法
if (x < 0)
//x = (x + 8 - 1) / 8; => (x + 7) >> 3;
//计算机默认向下取整
x = (x + 7) >> 3;
else
x >>= 3;
我们看看如下代码:
#include<stdio.h>
int main(int argc,char* args[])
printf("%d\\r\\n",argc/8);
return 0;
编译后会变为如下汇编指令
mov eax,dword ptr[argc]
移动到寄存器
cdq
把eax扩展为64位高位移动到edx
上.此处是为了处理正负数的情况
and edx ,7
如果为正数 edx比为0,如果为负数edx全为1。因此正数执行后 edx 为0,负数为7。
add eax,edx
想当与我们上面的(x + 7)
,不过后面的7可能为0
sar eax,3
eax算数右移动3位保证
除以非2倍数优化
我们可以利用以下公式
反推公式如下:
tip: M 不会被整除 因为2^n 是偶数 而c是非2的倍数,所以结果默认向下取整,这个结论在负数情况需要注意
其中 M是常量所以可以被编译器优化为编译常量,其中n至少为32(n越大结果越精确)
举例如下:
无符号除法
int main(unsigned int argc, char* args[])
//注意无符号除法结果为无符号、、argc是无符号数
printf("y ===>>> %d\\r\\n", argc / 3);
return 0;
对应的汇编语句:
其中0AAAAAAABh
就是的我们公式中M
; eax = M
mov eax,0AAAAAAABh
; 其中edx是存储高32位 eax是低32位 ret标记为我们的计算结果。argc为a 于是乎得到
; (edx,eax) = ret = eax * argc = aM
mul eax,dword ptr [argc]
;edx由于是高32,移动一位相当于ret移动33位,也就是 ret>>33 等价 edx=edx/(2^33)
shr edx,1
我们借用公式反推得到除数
可以看到上面整个算式中没有一个除法指令。
例外强悍的IDA pro可以帮我们快速识别
有符号除法
#include<stdio.h>
int main( int argc, char* args[])
//注意argc是有符号的
printf("y ===>>> %d\\r\\n", argc / 3);
return 0;
相较于无符号的除法多出以下汇编
0040104B mov eax,edx
;1F转为10进制31
0040104D shr eax,1Fh
00401050 add eax,edx
之所以多出几行行汇编是用于规避负数除法默认向下取整问题,所以为负数的时候需要加1.(参考上文)
;将a*m乘积的高位给eax
0040104B mov eax,edx
;1F转为10进制31
;右移动31后只剩下符号位也就是1或者0
;负数为1 正数为0
0040104D shr eax,1Fh
;如果是负数eax为1 那么进行加一补位
00401050 add eax,edx
M大于32位数的变形
#include<stdio.h>
int main(unsigned int argc, char* args[])
//注意argc是有符号的
printf("y ===>>> %d\\r\\n", argc / 7);
return 0;
上面的汇编转化为数学公式如下:
其中M为24924925h,而我们原来真正公式M为 2^32+M
很明显这数32寄存器
不足以存放,以及除以2^35也是同理
以上是关于编译器除法优化的主要内容,如果未能解决你的问题,请参考以下文章