【问题标题】:Delphi: fast Pos with 64-bitDelphi:64位的快速Pos
【发布时间】:2014-01-05 20:55:26
【问题描述】:

在 64 位中是否有与当前 32 位一样快的 Pos() 版本的代码?

据我了解,Delphi 中的 32 位版本(测试到 XE5)在很多年前就采用了 FastCode 汇编器版本,但对于 64 位,它使用的是 PurePascal 版本,速度慢了大约 5 到 10 倍。

一些测试,长循环中的相同过程:

32 位:65..90 毫秒

64 位:280..300 毫秒

【问题讨论】:

  • 请您提供您的测试程序
  • 您搜索的是子字符串还是单个字符?
  • 我搜索子字符串
  • 我对 purepascal 中的慢速 Pos 做了一份 QC 报告。请参阅Inefficient loop in Pos() for purepascal 以获得更快的实施。结论是使用 FastCoders purepascal 版本,PosSHAPosSHA2。您可以在 Fastcoders 页面上找到代码,fastcode.sourceforge.net
  • 您好,我有 FastCode 文件,但找不到 PosSHA/PosSHA2 函数,您能指点我吗?谢谢

标签: string delphi search 64-bit


【解决方案1】:

使用Fastcoders purepascal PosEx_Sha_Pas_2 算法(修改为适合x64):

function PosEx_Sha_Pas_2(const SubStr, S: string; Offset: Integer = 1): Integer;
Type
  PInteger =^Integer;
var
  len, lenSub: Integer;
  ch: char;
  p, pSub, pStart, pStop: pchar;
label
  Loop0, Loop4,
  TestT, Test0, Test1, Test2, Test3, Test4,
  AfterTestT, AfterTest0,
  Ret, Exit;
begin;
  pSub := pointer(SubStr);
  p := pointer(S);

  if (p = nil) or (pSub = nil) or (Offset < 1) then
  begin;
    Result := 0;
    goto Exit;
  end;

  lenSub := PLongInt(PByte(pSub) - 4)^ - 1; // <- Modified
  len := PLongInt(PByte(p) - 4)^; // <- Modified
  if (len < lenSub + Offset) or (lenSub < 0) then
  begin;
    Result := 0;
    goto Exit;
  end;

  pStop := p + len;
  p := p + lenSub;
  pSub := pSub + lenSub;
  pStart := p;
  p := p + Offset + 3;

  ch := pSub[0];
  lenSub := -lenSub;
  if p < pStop then
    goto Loop4;
  p := p - 4;
  goto Loop0;

Loop4:
  if ch = p[-4] then
    goto Test4;
  if ch = p[-3] then
    goto Test3;
  if ch = p[-2] then
    goto Test2;
  if ch = p[-1] then
    goto Test1;
Loop0:
  if ch = p[0] then
    goto Test0;
AfterTest0:
  if ch = p[1] then
    goto TestT;
AfterTestT:
  p := p + 6;
  if p < pStop then
    goto Loop4;
  p := p - 4;
  if p < pStop then
    goto Loop0;
  Result := 0;
  goto Exit;

Test3:
  p := p - 2;
Test1:
  p := p - 2;
TestT:
  len := lenSub;
  if lenSub <> 0 then
    repeat
      ;
      if (pSub[len] <> p[len + 1]) or (pSub[len + 1] <> p[len + 2]) then
        goto AfterTestT;
      len := len + 2;
    until len >= 0;
  p := p + 2;
  if p <= pStop then
    goto Ret;
  Result := 0;
  goto Exit;

Test4:
  p := p - 2;
Test2:
  p := p - 2;
Test0:
  len := lenSub;
  if lenSub <> 0 then
    repeat
      ;
      if (pSub[len] <> p[len]) or (pSub[len + 1] <> p[len + 1]) then
        goto AfterTest0;
      len := len + 2;
    until len >= 0;
  Inc(p);
Ret:
  Result := p - pStart;
Exit:
end;

以及使用大卫的测试用例(x64 版本)的结果:

System.Pos       18427
wcsstr            8122
PosEx_Sha_Pas_2   2282

对于 x32 版本,结果是:

System.Pos        2171
wcsstr            9634
PosEx_Sha_Pas_2   1868

结论:

PosEx_Sha_Pas_2 在 x64 位模式下几乎和Pos 在 x32 位模式下一样快。 此外,在 x32 位模式下,PosEx_Sha_Pas_2 似乎比 Pos 更快。

这里的所有测试都是 XE4 版本。


Improve purepascal System.Pos() 仍在东京 10.2 营业。


更新:

从 Delphi 11.0 Alexandria 开始,Pos() 的 purepascal 版本正在使用 Fastcoders PosEx_Sha_Pas_2.pas 版本。感谢@StefanGlienke 为实现这一目标所做的努力!

【讨论】:

  • 非常好。将此应用于自定义 xml 解析器,使用此 Pos 在 x64 中速度提高了 10 倍。谢谢!
  • 我怀疑我们需要更全面的测试才能说 SHA 版本优于 x86 asm Pos。也许子字符串的长度很重要。
  • @DavidHeffernan,我对 32 位模式进行了更多测试,如果在前 20-25 个字符内找到子字符串,System.Pos 似乎会更快一些。之后,SHA 版本更有效。子字符串的大小实际上并没有任何影响。
  • 我刚刚进行了一个较旧但类似的测试。在家里,在我的 Win7 VM 中,System.Pos 和 PosEx_Sha_Pas_2 之间没有太大区别,而我自己的则介于两者之间。在 Win64 中,PosEx_Sha_pas_2 比其他的要快。在这里,在真正的 Win7 中,Fastcode Pzurepascal 例程在 Win32 和 Win64 中总体上是最快的。
  • @hikari,要提高 AnsiStrings 的 64 位性能,请使用上面的函数。仅将String 更改为AnsiStringChar 更改为AnsiCharPChar 更改为PAnsiChar。与 Strings purepascal PosEx_Sha_Pas_2 例程相比,AnsiStrings 大约快 50% :-)
【解决方案2】:

在系统提供的msvcrt.dll中实现的C运行时函数wcsstr优于64位代码的Delphi RTLPos

{$APPTYPE CONSOLE}

uses
  SysUtils, Diagnostics;

function wcsstr(const str, strsearch: PWideChar): PWideChar; cdecl; external 'msvcrt.dll';

var
  i, j: Integer;
  Stopwatch: TStopwatch;
  str: string;
  P: PWideChar;

const
  N = 50000000;

begin
  str := 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do '
    + 'eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim '
    + 'ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut '
    + 'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit '
    + 'in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur '
    + 'sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt '
    + 'mollit anim id est laborum.';

  Stopwatch := TStopwatch.StartNew;
  for i := 1 to N do
  begin
    j := Pos('tempor', str);
    if j=0 then
      Beep;
  end;
  Writeln('Pos: ' + IntToStr(Stopwatch.ElapsedMilliseconds));

  Stopwatch := TStopwatch.StartNew;
  for i := 1 to N do
  begin
    P := wcsstr(PChar(str), 'tempor');
    if P=nil then
      Beep;
  end;
  Writeln('wcsstr: ' + IntToStr(Stopwatch.ElapsedMilliseconds));

  Readln;
end.

32 位发布版本

职位:1930 wcsstr: 6951

64 位发布版本

邮政编码:18384 wcsstr: 6701

有趣的是,32 位 Pos 显然实现得非常好。

我的系统在 i5-2300 上运行 Win7 x64。

【讨论】:

  • 我的测试结果(平均):32bit: pos=630, wcsstr=1330 ; 64位:pos=2730,wcsstr=1300
  • 似乎 FastCoders 的纯帕斯卡算法比 wcsstr 好。
猜你喜欢
  • 2023-03-07
  • 2011-01-22
  • 2023-03-22
  • 2012-07-07
  • 2016-04-11
  • 2012-05-12
  • 1970-01-01
  • 2011-11-22
  • 1970-01-01
相关资源
最近更新 更多