Delphi XE2 64位字符串例程运行时性能极低
我正在将一些应用程序从32位移植到64位delphi,它们进行大量的文本处理,并注意到处理速度发生了极大的变化。例如,使用一些过程进行了一些测试,这在64位上比编译到32位花费的时间要多200%(2000+毫秒,而约900毫秒) 这正常吗Delphi XE2 64位字符串例程运行时性能极低,delphi,64-bit,delphi-xe2,Delphi,64 Bit,Delphi Xe2,我正在将一些应用程序从32位移植到64位delphi,它们进行大量的文本处理,并注意到处理速度发生了极大的变化。例如,使用一些过程进行了一些测试,这在64位上比编译到32位花费的时间要多200%(2000+毫秒,而约900毫秒) 这正常吗 function IsStrANumber(const S: AnsiString): Boolean; var P: PAnsiChar; begin Result := False; P := PAnsiChar(S); while P^ &l
function IsStrANumber(const S: AnsiString): Boolean;
var P: PAnsiChar;
begin
Result := False;
P := PAnsiChar(S);
while P^ <> #0 do begin
if not (P^ in ['0'..'9']) then Exit;
Inc(P);
end;
Result := True;
end;
procedure TForm11.Button1Click(Sender: TObject);
Const x = '1234567890';
Var a,y,z: Integer;
begin
z := GetTickCount;
for a := 1 to 99999999 do begin
if IsStrANumber(x) then y := 0;//StrToInt(x);
end;
Caption := IntToStr(GetTickCount-z);
end;
函数IsStrANumber(const S:AnsiString):布尔;
var P:PAnsiChar;
开始
结果:=假;
P:=潘斯卡(S);
而P^#0确实开始了
如果没有(P^in['0'..'9']),则退出;
公司(P),;
结束;
结果:=真;
结束;
步骤t用于m11.按钮1单击(发件人:ToObject);
常数x='1234567890';
变量a,y,z:整数;
开始
z:=GetTickCount;
对于a:=1到9999999,请务必开始
如果ISSTRANNUMBER(x),则y:=0//stroint(x);
结束;
标题:=IntToStr(GetTickCount-z);
结束;
目前没有解决方案,因为64位的大多数字符串例程的代码都是用purpascal
定义的代码编译的,因此,它是纯Delphi,没有汇编程序,而32位的许多重要字符串例程的代码是由FastCode项目和汇编程序完成的
目前,没有64位的FastCode等价物,我假设开发团队无论如何都会尝试消除assembler,特别是因为他们正在转向更多的平台
这意味着生成代码的优化变得越来越重要。我希望宣布的向LLVM后端的迁移将大大加快大部分代码的速度,因此纯Delphi代码不再是这样的问题
很抱歉,没有解决方案,但可能是一个解释
更新
截至XE4,相当多的快速代码例程已经取代了我在上述段落中提到的未优化例程。它们通常仍然是
PUREPASCAL
,但它们代表了一个很好的优化。所以情况不像以前那么糟了。TStringHelper
和纯字符串例程在OSX中仍然显示一些错误和一些极慢的代码(特别是在涉及从Unicode到Ansi的转换或反之亦然的情况下),但是RTL的Win64部分似乎要好得多。尽量避免在循环中分配任何字符串
在您的情况下,可能涉及x64调用约定的堆栈准备。您是否尝试将IsStrANumber
声明为inline
我想这会使它更快
function IsStrANumber(P: PAnsiChar): Boolean; inline;
begin
Result := False;
if P=nil then exit;
while P^ <> #0 do
if not (P^ in ['0'..'9']) then
Exit else
Inc(P);
Result := True;
end;
procedure TForm11.Button1Click(Sender: TObject);
Const x = '1234567890';
Var a,y,z: Integer;
s: AnsiString;
begin
z := GetTickCount;
s := x;
for a := 1 to 99999999 do begin
if IsStrANumber(pointer(s)) then y := 0;//StrToInt(x);
end;
Caption := IntToStr(GetTickCount-z);
end;
函数IsStrANumber(P:PAnsiChar):布尔;内联;
开始
结果:=假;
如果P=nil,则退出;
而P^#0则
如果不是(P^in['0'..'9']),则
退出其他
公司(P),;
结果:=真;
结束;
步骤t用于m11.按钮1单击(发件人:ToObject);
常数x='1234567890';
变量a,y,z:整数;
s:翻译;
开始
z:=GetTickCount;
s:=x;
对于a:=1到9999999,请务必开始
如果是ISSTRANNUMBER(指针),则y:=0//stroint(x);
结束;
标题:=IntToStr(GetTickCount-z);
结束;
RTL的“纯帕斯卡”版本确实是这里缓慢的原因
请注意,与32位版本相比,FPC 64位编译器的性能更差。。。听起来Delphi编译器不是唯一的一个!64位并不意味着“更快”,不管市场怎么说!有时甚至恰恰相反(例如,已知JRE在64位上的速度较慢,并且在Linux中引入了一种新的指针大小)。代码可以这样编写,并具有良好的性能结果:
function IsStrANumber(const S: AnsiString): Boolean; inline;
var
P: PAnsiChar;
begin
Result := False;
P := PAnsiChar(S);
while True do
begin
case PByte(P)^ of
0: Break;
$30..$39: Inc(P);
else
Exit;
end;
end;
Result := True;
end;
英特尔(R)核心(TM)2处理器T5600@1.83GHz
- x32位:2730毫秒
- x64位:3260毫秒
- x32位:2979毫秒
- x64位:1794毫秒
function IsStrANumber(const S: AnsiString): Boolean; inline;
type
TStrData = packed record
A: Byte;
B: Byte;
C: Byte;
D: Byte;
E: Byte;
F: Byte;
G: Byte;
H: Byte;
end;
PStrData = ^TStrData;
var
P: PStrData;
begin
Result := False;
P := PStrData(PAnsiChar(S));
while True do
begin
case P^.A of
0: Break;
$30..$39:
case P^.B of
0: Break;
$30..$39:
case P^.C of
0: Break;
$30..$39:
case P^.D of
0: Break;
$30..$39:
case P^.E of
0: Break;
$30..$39:
case P^.F of
0: Break;
$30..$39:
case P^.G of
0: Break;
$30..$39:
case P^.H of
0: Break;
$30..$39: Inc(P);
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
end;
Result := True;
end;
英特尔(R)核心(TM)2处理器T5600@1.83GHz
- x32位:2199毫秒
- x64位:1934毫秒
- x32位:1170毫秒
- x64位:1279毫秒
如果你也应用Arnaud Bouchez所说的,你可以使它更快。64位的好处在于地址空间,而不是速度(除非你的代码受到可寻址内存的限制) 从历史上看,这种字符操作代码在更广泛的机器上总是比较慢。从16位8088/8086移动到32位386是正确的。将8位字符放入64位寄存器会浪费内存带宽和缓存 为了提高速度,您可以避免使用字符变量、使用指针、使用查找表、使用位并行(在一个64位字中操作8个字符)或使用SSE/SSE2。。。说明书显然,其中一些会使您的代码依赖于CPUID。此外,在调试时打开CPU窗口,并寻找编译器为您喜欢的静默字符串转换(尤其是在调用时)做一些愚蠢的事情
您可以尝试查看FastCode库中的一些本机Pascal例程。例如,PosEx_Sha_Pas_2虽然没有汇编程序版本快,但比RTL代码(32位)快。这里有两个函数。人们只检查正数。第二个也检查阴性。而且不限于大小。第二个比常规
Val
快4倍
function IsInteger1(const S: String): Boolean; overload;
var
E: Integer;
Value: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
function IsInteger2(const S: String): Boolean; inline;
var
I: Integer;
begin
Result := False;
I := 0;
while True do
begin
case Ord(S[I+1]) of
0: Break;
$30..$39:
case Ord(S[I+2]) of
0: Break;
$30..$39:
case Ord(S[I+3]) of
0: Break;
$30..$39:
case Ord(S[I+4]) of
0: Break;
$30..$39:
case Ord(S[I+5]) of
0: Break;
$30..$39:
case Ord(S[I+6]) of
0: Break;
$30..$39:
case Ord(S[I+7]) of
0: Break;
$30..$39:
case Ord(S[I+8]) of
0: Break;
$30..$39:
case Ord(S[I+9]) of
0: Break;
$30..$39:
case Ord(S[I+10]) of
0: Break;
$30..$39: Inc(I, 10);
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
end;
Result := True;
end;
function IsInteger3(const S: String): Boolean; inline;
var
I: Integer;
begin
Result := False;
case Ord(S[1]) of
$2D,
$30 .. $39:
begin
I := 1;
while True do
case Ord(S[I + 1]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 2]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 3]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 4]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 5]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 6]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 7]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 8]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 9]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 10]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 11]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 12]) of
0:
Break;
$30 .. $39:
case Ord(S[I + 13]) of
0:
Break;
$30 .. $39:
Inc(I, 13);
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
else
Exit;
end;
end;
else
Exit;
end;
Result := True;
end;
['0'..'9']中的测试
p^64位速度较慢
添加了一个内联函数,该函数具有对下/上边界的测试,而不是[]
测试中的,以及对空字符串的测试
function IsStrANumber(const S: AnsiString): Boolean; inline;
var
P: PAnsiChar;
begin
Result := False;
P := Pointer(S);
if (P = nil) then
Exit;
while P^ <> #0 do begin
if (P^ < '0') then Exit;
if (P^ > '9') then Exit;
Inc(P);
end;
Result := True;
end;
在32位中,主要的速度差是内联和p:=PAnsiChar(S)代码>将在分配指针值之前调用外部RTL例程进行零检查,而P:=指针代码>只分配指针
注意到这里的目标是测试字符串是否是数字,然后将其转换,
为什么不使用RTL,它一步完成所有工作,并处理标志和空白
通常在分析和优化例程时,大多数
x32 x64
--------------------
hikari 1420 3963
LU RD 1029 1060