VC 编译器将除法优化为乘法的策略(转自看雪bbs)
#include <stdio.h>
int main()
{
int a;
int b;
b = rand();
a = b/9;
printf("b = %d, a= %d", b , a);
getchar();
}
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char *envp)
.text:00401000 _main proc near ; CODE XREF: start+AFp
.text:00401000 E8 0E 02 00 00 call _rand
.text:00401005 8B C8 mov ecx, eax
.text:00401007 B8 39 8E E3 38 mov eax, 38E38E39h
.text:0040100C F7 E9 imul ecx
.text:0040100E D1 FA sar edx, 1
.text:00401010 8B C2 mov eax, edx
.text:00401012 C1 E8 1F shr eax, 1Fh
.text:00401015 03 D0 add edx, eax
.text:00401017 52 push edx
.text:00401018 51 push ecx
.text:00401019 68 30 70 40 00 push offset ??_C@_0O@DGOM@b?5?$DN?5?$CFd?0?5a?$DN?5?$CFd?$AA@ ; "b = %d, a= %d"
.text:0040101E E8 BF 01 00 00 call _printf
规则就是:
除数扩大X倍得到一个2^N。
除法就变为:被除数乘以X,再右移N位了。
A/B = AX /BX = AX >> N
比如 除以3,
会先乘以 0xAAAAAAAB ,再移位
硬件除法器的效率,不用说比加减,就是比乘法,也至少慢几倍。
硬件乘法器目前的研究已经相当成熟,比如十分常见的化莱氏树并行加法快速乘法器,对于设计好的乘法器,比如很多DSP中的乘加单元,在流水中可以一个周期完成一条乘法(当然是乘法器流水线处于理想状态下),在通常情况下,一条乘法指令可以在4~8个周期内完成。
但除法器要完成一次除法运算,在当今极度优化的硬件上,也至少需要十几个周期。
用乘法替换除法的近似算法,在库和编译器里随处可见。如果配置编译器按最大速度优化,那么编译器很可能会执行这种优化。而如果按最小体积优化,可能就使用硬件的除法指令了。
想起个有意思的事,大家在分析时经常见到51eb851f 这个值吧,这是编译器优化常见的一个值,再配合几次移位可以实现对进制100的除法。
我记得有一次看到一篇破文,作者不明白这个用法,说“这个操作数比较有意思,将它变为十进制为1374389535,不知是不是软件作者的手机号”,着实让我笑了一笑,觉得这个人太可爱,想像力真是太丰富了,可惜手机号是十一位,还差一位啊。
posted on 2010-01-17 10:20 Sunwayking 阅读(1305) 评论(1) 编辑 收藏 举报