如何在Delphi中使用现代CPU指令?(Java比Delphi快?)
一位朋友给我发了一份Delphi和Java最新版本的比较(如果您需要,可以使用源代码)。信不信由你,Java现在比Delphi快得多,因为Delphi编译器不会利用现代CPU指令!“慢”Java的重大突破 我的问题是:我们如何在Delphi中使用现代CPU指令而不用ASM 该项目是对上述问题的部分回答,但现在已被放弃。还有其他类似于FastCode的项目吗 这是另一篇显示Java和C的文章,它确实比Delphi快得多如何在Delphi中使用现代CPU指令?(Java比Delphi快?),delphi,delphi-xe2,delphi-2010,Delphi,Delphi Xe2,Delphi 2010,一位朋友给我发了一份Delphi和Java最新版本的比较(如果您需要,可以使用源代码)。信不信由你,Java现在比Delphi快得多,因为Delphi编译器不会利用现代CPU指令!“慢”Java的重大突破 我的问题是:我们如何在Delphi中使用现代CPU指令而不用ASM 该项目是对上述问题的部分回答,但现在已被放弃。还有其他类似于FastCode的项目吗 这是另一篇显示Java和C的文章,它确实比Delphi快得多 JAVA program d; {$APPTYPE CONSOLE} us
JAVA
program d;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.DateUtils;
var
t1, t2: TDateTime;
procedure xxx (n: integer; m: integer);
var
t: double;
i, j: integer;
d, r: double;
begin
t:= 0.0;
for j:= 1 to n do
begin
t:= t / 1000.0;
for i:= 1 to m do
begin
t:= t + i / 999999.0;
d:= t * t + i;
r:= (t + d) / (200000.0 * (i + 1));
t:= t - r;
end;
end;
writeln(t);
end;
begin
t1:= Now;
xxx(1, 999999999);
t2:= Now;
writeln(SecondsBetween(t2,t1));
t1:= Now;
xxx(1, 999999999);
t2:= Now;
writeln(SecondsBetween(t2,t1));
end.
import java.util.Date;
公共类j
{
公共静态无效xxx(整数n,整数m)
{
双t;
int i,j;
双d,r;
t=0.0;
对于(j=1;j
我们如何在Delphi中使用现代CPU指令(而不用ASM)
如果编译器不会发出您希望使用的CPU指令,那么除了自己生成所需的指令之外,别无选择,例如使用内联汇编程序。根据您的代码,32位Delphi编译器的缓慢之处在于浮点算术支持,这远远没有得到优化和复制FPU堆栈上有很多内容
在浮点运算方面,不仅Java JIT代码会更快,甚至现代JavaScript JIT编译器也会比Delphi好得多
并提供有关浮点的Delphi慢度的asm级别解释:
但是,如果您使用针对Win64平台的Delphi编译器,它将发出的不是x87而是SSE2操作码,而且速度会快得多,我怀疑这与Java JITted可执行文件相当
而且,就Java而言,任何Delphi可执行文件使用的内存都比JVM少得多,因此,在这里,Delphi可执行文件正处于最佳状态
如果您希望代码更快,请不要使用asm或低级优化技巧,而是更改算法。它可能比编译提示快一个数量级。专用过程将使用内联asm操作码实现-请查看此类低级黑客。但这并不容易掌握,通常需要继续Arnaud的观点——我实际上是用delphi为x86和x64编译的
32位编译器:
Unit1.pas.36: t:= t / 1000.0;
0051274D DD45F0 fld qword ptr [ebp-$10]
00512750 D835E4275100 fdiv dword ptr [$005127e4]
00512756 DD5DF0 fstp qword ptr [ebp-$10]
00512759 9B wait
Unit1.pas.37: for i:= 1 to m do
0051275A 8B45F8 mov eax,[ebp-$08]
0051275D 85C0 test eax,eax
0051275F 7E57 jle $005127b8
00512761 8945D0 mov [ebp-$30],eax
00512764 C745EC01000000 mov [ebp-$14],$00000001
Unit1.pas.39: t:= t + i / 999999.0;
0051276B DB45EC fild dword ptr [ebp-$14]
0051276E D835E8275100 fdiv dword ptr [$005127e8]
00512774 DC45F0 fadd qword ptr [ebp-$10]
00512777 DD5DF0 fstp qword ptr [ebp-$10]
0051277A 9B wait
Unit1.pas.40: d:= t * t + i;
0051277B DD45F0 fld qword ptr [ebp-$10]
0051277E DC4DF0 fmul qword ptr [ebp-$10]
00512781 DB45EC fild dword ptr [ebp-$14]
00512784 DEC1 faddp st(1)
00512786 DD5DE0 fstp qword ptr [ebp-$20]
00512789 9B wait
Unit1.pas.41: r:= (t + d) / (200000.0 * (i + 1));
0051278A DD45F0 fld qword ptr [ebp-$10]
0051278D DC45E0 fadd qword ptr [ebp-$20]
00512790 8B45EC mov eax,[ebp-$14]
00512793 40 inc eax
00512794 8945CC mov [ebp-$34],eax
00512797 DB45CC fild dword ptr [ebp-$34]
0051279A D80DEC275100 fmul dword ptr [$005127ec]
005127A0 DEF9 fdivp st(1)
005127A2 DD5DD8 fstp qword ptr [ebp-$28]
005127A5 9B wait
Unit1.pas.42: t:= t - r;
005127A6 DD45F0 fld qword ptr [ebp-$10]
005127A9 DC65D8 fsub qword ptr [ebp-$28]
005127AC DD5DF0 fstp qword ptr [ebp-$10]
005127AF 9B wait
Unit1.pas.43: end;
005127B0 FF45EC inc dword ptr [ebp-$14]
Unit1.pas.37: for i:= 1 to m do
005127B3 FF4DD0 dec dword ptr [ebp-$30]
005127B6 75B3 jnz $0051276b
Unit1.pas.44: end;
005127B8 FF45E8 inc dword ptr [ebp-$18]
64位编译器
Unit1.pas.36: t:= t / 1000.0;
000000000059F94E F20F104548 movsd xmm0,qword ptr [rbp+$48]
000000000059F953 F20F5E05BD000000 divsd xmm0,qword ptr [rel $000000bd]
000000000059F95B F20F114548 movsd qword ptr [rbp+$48],xmm0
000000000059F960 C7C001000000 mov eax,$00000001
000000000059F966 8B5568 mov edx,[rbp+$68]
000000000059F969 894544 mov [rbp+$44],eax
000000000059F96C 395544 cmp [rbp+$44],edx
000000000059F96F 7F73 jnle xxx + $C4
000000000059F971 83C201 add edx,$01
Unit1.pas.39: t:= t + i / 999999.0;
000000000059F974 F20F2A4544 cvtsi2sd xmm0,dword ptr [rbp+$44]
000000000059F979 F20F5E059F000000 divsd xmm0,qword ptr [rel $0000009f]
000000000059F981 F20F104D48 movsd xmm1,qword ptr [rbp+$48]
000000000059F986 F20F58C8 addsd xmm1,xmm0
000000000059F98A F20F114D48 movsd qword ptr [rbp+$48],xmm1
Unit1.pas.40: d:= t * t + i;
000000000059F98F F20F104548 movsd xmm0,qword ptr [rbp+$48]
000000000059F994 F20F594548 mulsd xmm0,qword ptr [rbp+$48]
000000000059F999 F20F2A4D44 cvtsi2sd xmm1,dword ptr [rbp+$44]
000000000059F99E F20F58C1 addsd xmm0,xmm1
000000000059F9A2 F20F114538 movsd qword ptr [rbp+$38],xmm0
Unit1.pas.41: r:= (t + d) / (200000.0 * (i + 1));
000000000059F9A7 F20F104548 movsd xmm0,qword ptr [rbp+$48]
000000000059F9AC F20F584538 addsd xmm0,qword ptr [rbp+$38]
000000000059F9B1 8B4544 mov eax,[rbp+$44]
000000000059F9B4 83C001 add eax,$01
000000000059F9B7 F20F2AC8 cvtsi2sd xmm1,eax
000000000059F9BB F20F590D65000000 mulsd xmm1,qword ptr [rel $00000065]
000000000059F9C3 F20F5EC1 divsd xmm0,xmm1
000000000059F9C7 F20F114530 movsd qword ptr [rbp+$30],xmm0
Unit1.pas.42: t:= t - r;
000000000059F9CC F20F104548 movsd xmm0,qword ptr [rbp+$48]
000000000059F9D1 F20F5C4530 subsd xmm0,qword ptr [rbp+$30]
000000000059F9D6 F20F114548 movsd qword ptr [rbp+$48],xmm0
Unit1.pas.43: end;
000000000059F9DB 83454401 add dword ptr [rbp+$44],$01
000000000059F9DF 395544 cmp [rbp+$44],edx
000000000059F9E2 7590 jnz xxx + $54
000000000059F9E4 90 nop
Unit1.pas.44: end;
000000000059F9E5 83454001 add dword ptr [rbp+$40],$01
000000000059F9E9 394D40 cmp [rbp+$40],ecx
000000000059F9EC 0F855CFFFFFF jnz xxx + $2E
000000000059F9F2 90 nop
Unit1.pas.45: writeln(t);
000000000059F9F3 488B0D9E150300 mov rcx,[rel $0003159e]
奇怪的是,在这种情况下,x87 fpu代码实际上快了约5%。结论可能只不过是Delphi的32位/x87编译器非常成熟,并且经过了很好的优化,64位编译器在性能方面可能还有一些改进的空间。我可以很容易地看到SSE代码可以选择的一些地方例如,i
可以存储在XMM寄存器中并重新使用,而不是每次使用cvtsi2sd
进行重新转换,d
可以保存在XMM寄存器中用于下一次计算,而不是存储和重新加载,等等
未对齐的MOV
s进出XMM寄存器的成本可能会惊人地高。实际的SSE计算速度更快,但过度的数据移动可能会影响分数。也许Java强制堆栈上16字节对齐?我知道MacOS会这样做,SSE使用对齐而不是u有一定的好处naligned移动(当然是以消耗更多堆栈空间为代价)
比如说
fild
:1个操作,9个延迟(x87)
cvtsi2sd
:2个op,12个延迟(SSE)
或
fld
:1个操作,4个延迟(x87)
movsd
[r,m]:2op,4延迟(SSE)
Delphi的编译器在发出SSE指令的同时,似乎仍在以与x87单元类似的方式处理工作流,这不一定是最好的方式。在这两种情况下,David都是正确的-编译器就是这样。你不能做任何事情来改变它
在我需要快速数学例程的地方,我仍然自己在ASM中编写它们-这通常比任何编译器所能做的都要好,因为您可以根据正在进行的精确计算自定义行为。我有旧的传统32位应用程序,带有手动调整的SSE3 ASM算法,用于复数算术和矩阵运算关键是你不需要优化所有的东西-你只需要优化瓶颈。这是一个需要注意的重要问题。我将在这里回答一个元问题:“为什么Delphi编译器不能使用更现代的CPU指令,为什么Java可以?”
基本上,有两种编译代码的方法:
在开发人员机器上预编译
在目标计算机上进行后期编译(包括JITed)
示例1.包括Delphi、C/C++等
示例2.包括Java、.NET、JavaScript等
预编译环境
预编译环境让您只编译一次代码,然后在目标计算机上运行。已编译程序不能在使用比已编译程序使用的旧指令集的计算机上运行。最低要求是编译器能做得最好的最低要求,以及所有目标机的最低体系结构。如果您不知道对于您的目标机器,它受到编译器的限制
x32 x64 Java(x64)
--------------------------
Original algorithm 23417ms 22293ms 22045ms
Updated algorithm 22362ms 14059ms 15507ms
编译后环境
编译后的环境在目标机器上编译。您不必知道它运行的是什么体系结构:在目标机器上运行的编译器需要知道它支持什么才能最大限度地利用它。最低要求是编译器可以做得最好的最低要求,以及目标机器的体系结构
原因是在后编译、JIT或解释的语言环境中,编译器实际上是在
public static void xxy(int n, int m)
{
double t;
int i, j;
double d, r, ii;
t = 0.0;
for (j = 1; j <= n; j++)
{
t = t / 1000.0;
ii = 1.0;
for (i = 1; i <= m; i++)
{
t = t + ii / 999999.0;
d = t * t + ii;
ii = ii + 1.0;
r = (t + d) / (200000.0 * ii);
t = t - r;
}
}
System.out.println(t);
}
x32 x64 Java(x64)
--------------------------
Original algorithm 23417ms 22293ms 22045ms
Updated algorithm 22362ms 14059ms 15507ms
Project19.dpr.11: begin
000000000046ABC0 55 push rbp
000000000046ABC1 4883EC20 sub rsp,$20
000000000046ABC5 488BEC mov rbp,rsp
Project19.dpr.12: t:= 0.0;
000000000046ABC8 F20F1005B0000000 movsd xmm0,qword ptr [rel $000000b0]
000000000046ABD0 C7C001000000 mov eax,$00000001
000000000046ABD6 4189C8 mov r8d,ecx
000000000046ABD9 89C1 mov ecx,eax
000000000046ABDB 413BC8 cmp ecx,r8d
000000000046ABDE 7F7B jnle xxx + $9B
000000000046ABE0 4183C001 add r8d,$01
Project19.dpr.15: t:= t / 1000.0;
000000000046ABE4 F20F5E059C000000 divsd xmm0,qword ptr [rel $0000009c]
Project19.dpr.16: ii := 1.0;
000000000046ABEC F20F100D9C000000 movsd xmm1,qword ptr [rel $0000009c]
000000000046ABF4 C7C001000000 mov eax,$00000001
000000000046ABFA 4189D1 mov r9d,edx
000000000046ABFD 413BC1 cmp eax,r9d
000000000046AC00 7F50 jnle xxx + $92
000000000046AC02 4183C101 add r9d,$01
Project19.dpr.19: t:= t + ii / 999999.0;
000000000046AC06 660F28D1 movapd xmm2,xmm1
000000000046AC0A F20F5E1586000000 divsd xmm2,qword ptr [rel $00000086]
000000000046AC12 F20F58C2 addsd xmm0,xmm2
Project19.dpr.20: d:= t * t + ii;
000000000046AC16 660F28D0 movapd xmm2,xmm0
000000000046AC1A F20F59D0 mulsd xmm2,xmm0
000000000046AC1E F20F58D1 addsd xmm2,xmm1
Project19.dpr.21: ii := ii + 1.0;
000000000046AC22 F20F580D66000000 addsd xmm1,qword ptr [rel $00000066]
Project19.dpr.22: r:= (t + d) / (200000.0 * ii);
000000000046AC2A 660F28D8 movapd xmm3,xmm0
000000000046AC2E F20F58DA addsd xmm3,xmm2
000000000046AC32 660F28D1 movapd xmm2,xmm1
000000000046AC36 F20F591562000000 mulsd xmm2,qword ptr [rel $00000062]
000000000046AC3E F20F5EDA divsd xmm3,xmm2
000000000046AC42 660F29DA movapd xmm2,xmm3
Project19.dpr.23: t:= t - r;
000000000046AC46 F20F5CC2 subsd xmm0,xmm2
Project19.dpr.24: end;
000000000046AC4A 83C001 add eax,$01
000000000046AC4D 413BC1 cmp eax,r9d
000000000046AC50 75B4 jnz xxx + $46
000000000046AC52 90 nop
Project19.dpr.25: end;
000000000046AC53 83C101 add ecx,$01
000000000046AC56 413BC8 cmp ecx,r8d
000000000046AC59 7589 jnz xxx + $24
000000000046AC5B 90 nop
Project19.dpr.26: WriteLn(t);
000000000046AC5C 488B0DC5100100 mov rcx,[rel $000110c5]
000000000046AC63 660F29C1 movapd xmm1,xmm0
000000000046AC67 E874D7F9FF call @Write0Ext
000000000046AC6C 4889C1 mov rcx,rax
000000000046AC6F E88CD7F9FF call @WriteLn
000000000046AC74 E877AFF9FF call @_IOTest
Project19.dpr.27: end;
000000000046AC79 488D6520 lea rsp,[rbp+$20]