Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: fast Pos with 64-bit

Is there any code for a Pos() version that's as fast in 64-bit than the current 32-bit?

To my understanding, the 32-bit version in Delphi (tested up to XE5) adopted the FastCode assembler versions many years ago, but for 64-bit it uses a PurePascal version, which is around 5 to 10 times slower.

Some tests, same procedure in a long loop:

32-bit: 65..90ms

64-bit: 280..300ms

like image 379
hikari Avatar asked Dec 02 '22 17:12

hikari


1 Answers

Using Fastcoders purepascal PosEx_Sha_Pas_2 algorithm (modified to fit 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;

And the result using David's test case (x64 release):

System.Pos       18427
wcsstr            8122
PosEx_Sha_Pas_2   2282

For the x32 release the results are:

System.Pos        2171
wcsstr            9634
PosEx_Sha_Pas_2   1868

Conclusion:

PosEx_Sha_Pas_2 is almost as fast in x64 bit mode as Pos in x32 bit mode. Additionally it seems as PosEx_Sha_Pas_2 is faster than Pos in x32 bit mode.

All tests here is with the XE4 version.


Improve purepascal System.Pos() still open in Tokyo 10.2.


Update:

As of Delphi 11.0 Alexandria, the purepascal version of Pos() is using the Fastcoders PosEx_Sha_Pas_2.pas version. Thanks @StefanGlienke for your effort in making this happen!

like image 82
LU RD Avatar answered Dec 10 '22 02:12

LU RD