gpt4 book ai didi

delphi - Win32/64 中 Math.Sum 的不同优化

转载 作者:行者123 更新时间:2023-12-02 11:19:58 25 4
gpt4 key购买 nike

我有以下代码

const
NumIterations = 10000000;
var
i, j : Integer;
x : array[1..100] of Double;
Start : Cardinal;
S : Double;
begin
for i := Low(x) to High(x) do x[i] := i;

Start := GetTickCount;
for i := 1 to NumIterations do S := System.Math.Sum(x);
ShowMessage('Math.Sum: ' + IntToStr(GetTickCount - Start));

Start := GetTickCount;
for i := 1 to NumIterations do begin
S := 0;
for j := Low(x) to High(x) do S := S + x[j];
end;
ShowMessage('Simple Sum: ' + IntToStr(GetTickCount - Start));
end;

当为 Win32 编译时,Math.Sum 比简单循环快得多,因为 Math.Sum 是用汇编程序编写的,并使用四重循环展开。

但是当针对 Win64 进行编译时,Math.Sum 比简单循环慢得多,因为在 64 位 Math.Sum 中使用 Kahan 求和。这是一种精度优化,可最大限度地减少求和过程中的错误堆积,但速度比简单循环要慢得多。

即当为 Win32 编译时,我得到了针对速度进行优化的代码,当为 Win64 编译相同的代码时,我得到了针对准确性进行优化的代码。这并不完全是我天真的所期望的。

Win32/64 之间的这种差异有什么合理的原因吗? Double 始终为 8 字节,因此在 Win32/64 中精度应该相同。

在当前版本的 Delphi 中,Math.Sum 是否仍然以相同的方式实现(Win32 中的汇编器和循环展开,Win64 中的 Kahan 求和)?我使用Delphi-XE5。

最佳答案

Is Math.Sum still implemented identically (Assembler and loop unrolling in Win32, Kahan summation in Win64) in current versions of Delphi? I use Delphi-XE5.

是(Delphi 10.3.2)。

Is there any sensible reason for this difference between Win32/64? Double is always 8 byte, so the accuracy should be identical in Win32/64.

32位Delphi for Win32使用旧的FPU,而64位编译器使用SSE指令。当 XE2 中引入 64 位编译器时,许多旧的汇编例程并未移植到 64 位。相反,一些例程被移植为具有与其他现代编译器类似的功能。

<小时/>

您可以通过引入 Kahan summation function 来稍微增强 64 位实现。 :

program TestKahanSum;

{$APPTYPE CONSOLE}

uses
System.SysUtils,Math,Diagnostics;

function KahanSum(const input : TArray<Double>): Double;
var
sum,c,y,t : Double;
i : Integer;
begin
sum := 0.0;
c := 0.0;
for i := Low(input) to High(input) do begin
y := input[i] - c;
t := sum + y;
c := (t - sum) - y;
sum := t;
end;
Result := sum;
end;

var
dArr : TArray<Double>;
res : Double;
i : Integer;
sw : TStopWatch;
begin
SetLength(dArr,100000000);
for i := 0 to High(dArr) do dArr[i] := Pi;
sw := TStopWatch.StartNew;
res := Math.Sum(dArr);
WriteLn('Math.Sum:',res,' [ms]:',sw.ElapsedMilliseconds);
sw := TStopWatch.StartNew;
res := KahanSum(dArr);
WriteLn('KahanSum:',res,' [ms]:',sw.ElapsedMilliseconds);
sw := TStopWatch.StartNew;
res := 0;
for i := 0 to High(dArr) do res := res + dArr[i];
WriteLn('NaiveSum:',res,' [ms]:',sw.ElapsedMilliseconds);
ReadLn;
end.
<小时/>

64 位:

Math.Sum: 3.14159265358979E+0008 [ms]:492
KahanSum: 3.14159265358979E+0008 [ms]:359
NaiveSum: 3.14159265624272E+0008 [ms]:246

32 位:

Math.Sum: 3.14159265358957E+0008 [ms]:67
KahanSum: 3.14159265358979E+0008 [ms]:958
NaiveSum: 3.14159265624272E+0008 [ms]:277

15位圆周率是3.14159265358979

在此示例中,32 位数学汇编例程精确到 13 位数字,而 64 位数学例程精确到 15 位数字。

<小时/>

结论:

  • 64 位实现速度较慢(与简单求和相比慢两倍),但比 32 位数学例程更准确。

  • 引入增强的 Kahan 求和例程可将性能提高 35%。

关于delphi - Win32/64 中 Math.Sum 的不同优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57141737/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com