DELPHI进行浮点运算效率的问题,如何改进呢?

jojoxyz 2017-01-24 07:21:14
一段简单的代码就是循环和计算,在同一机器上delphi7运行时间7秒,VC运行时间0.2秒。听说DELPHI进行浮点运算效率较低,有没有改进的方式?

如果屏蔽这句lat:=((pi/4-arctan(power(b/a,1/n)))*2)*180/pi;运算时间差不多可以减少一半。
示意代码如下:


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,math;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure xy_to_jw(const x,y,c,k:integer;const n,zxjd,a,zxy,xs:double);
end;

var
Form1: TForm1;
lon,lat:double;

implementation

{$R *.dfm}

procedure TForm1.xy_to_jw(const x,y,c,k:integer;const n,zxjd,a,zxy,xs:double);
var
b:double;
begin
if y+zxy-k/2<>0 then
lon:=arctan((x-c/2)/(y+zxy-k/2))*180/pi/n+zxjd
else
lon:=90/n+zxjd;
b:=(x-c/2)*(x-c/2)+(y+zxy-k/2)*(y+zxy-k/2);
b:=xs*sqrt(b);
lat:=((pi/4-arctan(power(b/a,1/n)))*2)*180/pi;
end;

procedure TForm1.Button1Click(Sender: TObject);
var i,j :integer;
t1,t2,t3:int64;
lon,lat:double;
begin
queryperformancecounter(t1);
for j:=0 to 4000-1 do
begin
for i:=0 to 6000-1 do
begin
xy_to_jw(i,j,6000,4800,0.72,115,11423.8,7529,0.922);
end;
end;
queryperformancecounter(t2);
queryperformancefrequency(t3);
self.Caption :=format('%.1f',[(t2-t1)/t3])+'秒';
end;

end.
...全文
1460 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
jojoxyz 2017-02-19
  • 打赏
  • 举报
回复
抛砖引玉完毕,终结此贴,谢谢大家参与
  • 打赏
  • 举报
回复
salaox 2017-02-12
  • 打赏
  • 举报
回复
涉及复杂算法的,我一般用Intel Fortran做成DLL,让Delphi调用,或者用文本文件交换数据。
  • 打赏
  • 举报
回复
引用 17 楼 jojoxyz 的回复:
楼上牛啊,期待一下
已经搞定了,XE5测试通过,但是D7支持没搞定,折腾了好几天,还是不行。 首先D7不支持coff格式的obj,用objconv转换之后还是不认,只能反汇编再重新生成,用ml生成的omf它都不认,幸好我这汇编器一大堆,挨个试,jwasm生成的可以,连接成功,又发现D7连接外部obj的函数无法使用别名(我很长时间不用D7,都忘了),___libm_sse2_cosf这么长的函数名怎么用啊,只好再修改asm重新生成。测试的时候又出现异常,一部分函数可以用,另外的异常,调试发现是其中的一部分数据要求16字节对齐,但是连接到D7程序中保证不了,反复修改还是不行,只好放弃。 测试表明,虽然使用了intel的库函数实现,但是整个函数的执行速度还是没有用intel C++编译的快,首先整体代码的优化没有intel做得好,其次这些是库内部函数,在32位下并不使用标准调用约定,是我写了包装函数完成参数转换的,多了一次调用,还有浮点结果的压栈操作,另外这只是针对标准math库的优化,intel还有svml,可以一次计算2、4、8个超越函数,就凭这一点,同样开AVX优化,xy_to_jw这个函数用intel C++编译的性能是VC++编译的三倍,我看了一下svml_dispmt.lib,里面有6800多个.obj,intel真是大手笔,这工作量,光优化库函数就要几十人吧。
salaox 2017-02-12
  • 打赏
  • 举报
回复
曾经用Delphi实现一个复平面的超几何函数,计算结果从小数点后第6位开始和FORTRAN的计算结果有差异。
  • 打赏
  • 举报
回复
Delphi是支持10进制运算的(包括浮点运算),通过TBcd类型,好像可以支持到最多64位小数(10进制)。但是只能做简单算数运算,加减乘除等,三角函数之类还不能支持。比intel DFP 10进制浮点库还是要差一些。
BambooCaep 2017-02-07
  • 打赏
  • 举报
回复
140.2609897658834581813333810108094318577 19.82914676497599251957738664525582041005
jojoxyz 2017-02-06
  • 打赏
  • 举报
回复
楼上牛啊,期待一下
  • 打赏
  • 举报
回复
我试了一下,可以把intel C++的math库功能(至少是一部分)连接到Delphi程序中使用,是静态连接,不需要DLL。我先把这些函数封装成一个单元,方便使用,过几天发出来。
武稀松 2017-02-02
  • 打赏
  • 举报
回复
提供两个思路, 1.如果你要的精度不是特别高,可以把浮点数先转成整数,最后再除以系数变成浮点数,中间运算全是整数运算。速度跟VC差不多 2.如果你的a,b,n三个变量组合不多比如只有几万几十万的组合,可以先计算好放到表里面,做查表。几乎可以0ms
lyhoo163 2017-01-27
  • 打赏
  • 举报
回复
lyhoo163 2017-01-26
  • 打赏
  • 举报
回复
对VC 0.2秒,表示怀疑? 可能是VC中,有优化代码功能,对于循环语句,重复计算只计算1次。(无意义的循环语句实行优化)
msdtx 2017-01-26
  • 打赏
  • 举报
回复
用 IDA 扣出来,怎么用都随你
  • 打赏
  • 举报
回复
引用 11 楼 lyhoo163 的回复:
对VC 0.2秒,表示怀疑? 可能是VC中,有优化代码功能,对于循环语句,重复计算只计算1次。(无意义的循环语句实行优化)
intel C++如果开AVX优化,在i7-6700k上确实可以做到0.24秒,但是VC++还不行,只能到0.7x的程度。
  • 打赏
  • 举报
回复
引用 8 楼 jojoxyz 的回复:
to DelphiGuy:

您说的办法我试了,结果如下:

1. xy_to_jw可以改成全局函数。
结果:对速度提升影响不大

2. 可以inline,procedure xy_to_jw(const x,y,c,k:integer;const n,zxjd,a,zxy,xs:double); inline;
结果:由于是delphi7不支持,换成XE8进行测试,对速度提升影响不大

3. 诸如k/2这种运算,如果直接从C代码中抄来的,算法并不一致。在C中/是整除,在Delphi中/是浮点除,性能相差几十倍,整除要用div。
结果:将/2直接换成数字,速度提升不到1秒

将上述代码在QT中测试,用时0.8秒,统一机器速度相差近10倍。

PS:其实我提问的原因不是想对比各软件性能,而是想知道DELPHI7在无法进行浮点运算优化的情况下,如何采取其他办法提升运行速度。至于1楼说的VC做DLL再调用,让用delphi的人情何以堪啊......


1. D7是15年前的产品,拿它和现在的编译器比,就象拿80年代的Turbo C和D7比优化,没有必要也没有意义。
2. Delphi高版本的优化水平是可以接受的,虽然还是慢一些,和VC++、intel C++是可以比较的,实际上差距主要在库函数的实现代码,而不是编译器本身。
3. Delphi代码的优势是运算精度高,32位编译器对浮点运算的中间结果都是以扩展精度保存的,所以最终结果能保持最高的精度,64位编译器对浮点运算没有使用FPU指令,而是用SSEn指令仿真的,由于SSE中没有扩展精度数据类型,最高只到double,所以凡是多步double运算,最终结果必然达不到double精度(除非恰好每步的运算结果都是2的若干次幂),所有64位编译器生成的代码都不例外,但是Delphi代码的运算结果仍然比C++代码的运算结果多两位精度,就这个例子而言,即便C++编译器开/fp:precise,intel C++开/fp:extended,运算结果仍然没有达到Delphi代码的精度。
4. "将上述代码在QT中测试,用时0.8秒"是什么概念,Qt只是库,你用的编译器、编译选项、硬件需要说明一下。
5. 前面说了,差距主要在库函数的实现代码,象intel C++默认连接的libmmt.lib,里面库函数的实现是手工优化的汇编代码,诸如__libm_sse2_atan、__libm_sse2_pow之类的,VC++的库中也有,但是优化水平比intel C++差一些,理论上也可以把这些库函数的obj从lib中解出来,连接到Delphi程序中使用,但是这些内部函数不使用标准调用约定,还需要研究一下。

  • 打赏
  • 举报
回复
VC代码呢,没有对应的代码无法比较。 就你的代码: 1. xy_to_jw可以改成全局函数。 2. 可以inline,procedure xy_to_jw(const x,y,c,k:integer;const n,zxjd,a,zxy,xs:double); inline; 3. 诸如k/2这种运算,如果直接从C代码中抄来的,算法并不一致。在C中/是整除,在Delphi中/是浮点除,性能相差几十倍,整除要用div。 4. 由于lon,lat:double;只保留最后一次xy_to_jw调用的结果,所以 for j:=0 to 4000-1 do begin for i:=0 to 6000-1 do begin xy_to_jw(i,j,6000,4800,0.72,115,11423.8,7529,0.922); end; end; 和 xy_to_jw(5999,3999,6000,4800,0.72,115,11423.8,7529,0.922); 是等效的,某些编译器的激进优化结果,会把循环整个消去,只保留一次调用,看起来很有优势,但是在真实应用中通常不会出现这种代码。
pathletboy 2017-01-25
  • 打赏
  • 举报
回复
引用 3 楼 jojoxyz 的回复:
Set8087cw这句加了,对速度没有影响
能否发出对应的VC++测试代码?
jojoxyz 2017-01-25
  • 打赏
  • 举报
回复
Set8087cw这句加了,对速度没有影响
lyhoo163 2017-01-25
  • 打赏
  • 举报
回复
还有一种办法,通过汇编语言写代码,试试?
jojoxyz 2017-01-25
  • 打赏
  • 举报
回复
to DelphiGuy: 您说的办法我试了,结果如下: 1. xy_to_jw可以改成全局函数。 结果:对速度提升影响不大 2. 可以inline,procedure xy_to_jw(const x,y,c,k:integer;const n,zxjd,a,zxy,xs:double); inline; 结果:由于是delphi7不支持,换成XE8进行测试,对速度提升影响不大 3. 诸如k/2这种运算,如果直接从C代码中抄来的,算法并不一致。在C中/是整除,在Delphi中/是浮点除,性能相差几十倍,整除要用div。 结果:将/2直接换成数字,速度提升不到1秒 将上述代码在QT中测试,用时0.8秒,统一机器速度相差近10倍。 PS:其实我提问的原因不是想对比各软件性能,而是想知道DELPHI7在无法进行浮点运算优化的情况下,如何采取其他办法提升运行速度。至于1楼说的VC做DLL再调用,让用delphi的人情何以堪啊......
加载更多回复(4)

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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