Sunwayking

导航

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编辑  收藏  举报