呃,不小心写了个比FDIV指令更快的浮点除法函数

阿呆_ 2010-05-31 11:43:57
加精
最近无聊中一不留神又在网上看到了Quake中的那段著名的Inverse Square Root代码,忽然产生了一个冲动,将那段代码改成了一个计算浮点除法的函数,不幸就得到了一段比两个浮点数直接相除更快的代码。先说说我是怎么改造的,这段Quake代码计算的是某个浮点数的平方根的倒数,如果我们用这段代码对除数进行处理,得到的结果平方一下,再乘以被除数,那么结果当然就是两个数直接相除的值了。看上去很白痴的行为,可是结果却出人意料,绕这么一大圈来得到结果,比直接两个数相除还快。
简单测试了一下,又发现了另一件有趣的事情,在Intel Core Duo cpu上我的函数大约快50%,可是在Intel P4上我的函数竟然快了220%(很狼狈的结果,在两种cpu上我的函数执行时间几乎相同),看来传说中P4有时比P3还慢的谣言并不是瞎说。

下面详细解说一下我的改造:

先看一下传说中的那个Inverse sqrt函数的相当神奇的源码:
float InvSqrt(float x){
float xhalf = 0.5f * x;
int i = *(int*)&x; // store floating-point bits in integer
i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method
x = *(float*)&i; // convert new bits into float
x = x*(1.5f - xhalf*x*x); // One round of Newton's method
return x;
}

改造第一步,当然是将其翻译成delphi:
function InvSqrt(x: Single): Single;
var
xhalf: Single;
i: Integer;
begin
xhalf := x * 0.5;
i := PInteger(@xhalf)^;
i := $5f3759d5 - (i shr 1);
x := PSingle(@i)^;
Result := x * (1.5 - xhalf*x*x);
end;

由于本人对执行效率的变态追求(并不是一个好习惯),我又进一步将其改成了汇编:
var
OneHalf: DWORD = $3FC00000;

function f_invsqrt(v: Single): Single;
asm
MOV EAX, DWORD PTR [EBP+8] // i := PInteger(@x)^;
SUB EAX, $800000
PUSH EAX // xhalf := x*2
ADD EAX, $800000
MOV EDX, $5F3759D5
SHR EAX, 1
SUB EDX, EAX // i := 5f3759d5-(i shr 1)
PUSH EDX
FLD DWORD PTR [ESP] // x := PSingle(@i)^
FLD ST(0)
FMUL ST(0), ST(0)
FMUL DWORD PTR [ESP+4]
FSUBR DWORD PTR OneHalf
FMULP // result := x * (1.5 - xhalf*x*x);
ADD ESP, 8
end;



再将其改成除法, 用delphi写的话就是(当然,这段代码绝对比直接除慢得多的多的多):
function f_div(x, y: Single): Single;
begin
if y<0 then
begin
y := -y;
x := -x;
end;
result :=f_invsqrt(y) * f_invsqrt(y) * x;
end;


最后优化后的代码如下:

function f_div(v1, v2: Single): Single;
asm
/* 调整v1, v2的值: v2取绝对值并相应调整v1的正负 */
MOV EAX, DWORD PTR [EBP+8]
MOV EDX, $80000000
AND EDX, EAX
AND EAX, $7FFFFFFF
XOR DWORD PTR [EBP+12], EDX
FLD DWORD PTR [EBP+12]
/* 计算v2的Inverse sqrt再平方再乘以v1 */
SUB EAX, $800000
PUSH EAX
ADD EAX, $800000
MOV EDX, $5F3759D5
SHR EAX, 1
SUB EDX, EAX
PUSH EDX
FLD DWORD PTR [ESP]
FMUL ST(0), ST(0)
FMUL ST(1), ST(0)
FMUL DWORD PTR [ESP+4]
FSUBR DWORD PTR OneHalf
FMUL ST(0), ST(0)
FMULP
ADD ESP, 8
end;


...全文
6945 192 打赏 收藏 转发到动态 举报
写回复
用AI写文章
192 条回复
切换为时间正序
请发表友善的回复…
发表回复
fang88 2012-10-28
  • 打赏
  • 举报
回复
俺也来膜拜一下!
水月CC 2011-03-19
  • 打赏
  • 举报
回复
这个确实需要围观下
tataishan 2010-06-25
  • 打赏
  • 举报
回复
围观。。。
wxhysoftsodc 2010-06-08
  • 打赏
  • 举报
回复
牛13帖
lb438912713 2010-06-07
  • 打赏
  • 举报
回复
学习学习
lb438912713 2010-06-07
  • 打赏
  • 举报
回复
学习学习
  • 打赏
  • 举报
回复
看来阿呆兄在潜心研究新算法... :)
linghengmao 2010-06-07
  • 打赏
  • 举报
回复
顶礼膜拜楼主。
l172025396 2010-06-06
  • 打赏
  • 举报
回复
不太懂~汗,还是路过吧
luoaiwen1314 2010-06-06
  • 打赏
  • 举报
回复
隐藏了,嘿嘿·····
nhconch 2010-06-06
  • 打赏
  • 举报
回复
有这种事
lolita22 2010-06-06
  • 打赏
  • 举报
回复
对萨达是方法对方的三个发生大幅
hau1300 2010-06-06
  • 打赏
  • 举报
回复
学习的...
zongmekeneng 2010-06-05
  • 打赏
  • 举报
回复
路过……
wwwtre 2010-06-05
  • 打赏
  • 举报
回复
楼主人才啊
可惜我不懂汇编语言
hardxin 2010-06-05
  • 打赏
  • 举报
回复
果然变态。
zhangy313 2010-06-05
  • 打赏
  • 举报
回复
测试x一下!
有没有C的啊
about_it 2010-06-05
  • 打赏
  • 举报
回复
膜拜呀,膜拜
chbybby 2010-06-05
  • 打赏
  • 举报
回复
准备学习软件开发
村过去 2010-06-05
  • 打赏
  • 举报
回复
我是进来看图的,发现居然有码,因此mark!
加载更多回复(171)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧