Delphi:什么是更快的纯Pascal方法来查找Unicode字符串中字符的位置?

时间:2015-08-09 17:07:25

标签: delphi

背景

稍后添加

我已经制作了一个纯粹的Pascal函数来查找Unicode字符串中字符的位置,如下所示:

function CharPosEx(const chChr: Char; const sStr: string;
    const iOffset: Integer=1): Integer;
var
  PStr   : PChar;
  PRunIdx: PChar;
  PEndIdx: PChar;
  iLenStr: Integer;

begin
  Result := 0;
  iLenStr := Length(sStr);
  if (iLenStr = 0) or (iOffset <= 0) or (iOffset > iLenStr) then Exit;

  PStr := Pointer(sStr);
  PEndIdx := @PStr[iLenStr - 1];
  PRunIdx := @PStr[iOffset - 1];

  repeat
    if PRunIdx^ = chChr then begin
      Result := PRunIdx - PStr + 1;
      Exit;
    end;
    Inc(PRunIdx);
  until PRunIdx > PEndIdx;
end;

我决定不使用内置StrUtils.PosEx(),因为我想基于UTF16_CharPosEx的优化纯Pascal函数创建CharPosEx函数。我正试图找到一个更快的通用解决方案,如Fastcode Project的纯粹Pascal方法。

原始陈述

根据问题的接受答案 Delphi: fast Pos with 64-bit ,找到字符串中子字符串位置的最快的纯Pascal函数是PosEx_Sha_Pas_2() CharPos() 3}}

对于最快的纯Pascal函数来查找字符串中字符的位置,我注意到Fastcode ProjectCharPosIEx()CharPosEY()CharPosRev()表示从左到右匹配,以及从右到左匹配的CharPos()

然而,问题是所有Fastcode函数都是在Delphi 2009之前开发的,Delphi 2009是第一个支持Unicode的Delphi版本。

我对CharPosEY()CharPos感兴趣。我想重新对它们进行基准测试,因为现在有一些无用的优化技术,例如偶尔在Fastcode函数中实现的循环展开技术。

但是,我不能为每个CharPos()家庭挑战重新编译基准测试项目,因为我一直在使用Delphi XE3,因此我不能断定哪一个是最快的。

问题

这里的任何人都知道或可以推断哪一个是针对每个提到的Fastcode挑战最快的纯Pascal实现,特别是对于CharPosEY()UnicodeString

欢迎Fastcode Project解决方案中的其他方法。

注释

  • 我在这里使用的 Unicode字符串术语指的是一个字符串,其类型为plot(x = seq(size), means, pch = 20, **ylim=c(?,?)**, ylab="Mean", main="Analysis of Means", xaxt = 'n') axis(1,seq(size)) segments(seq(size), u, seq(size), means) lines(seq(1, 15, 1), rep(u_vec + seg, each = 1), type = "S") lines(seq(1, 15, 1), rep(u_vec - seg, each = 1), type = "S") abline(h=u) ,无论其编码方案如何。
  • 如果编码方案很重要,我的意思是固定宽度的16位编码方案(UCS-2)。

1 个答案:

答案 0 :(得分:3)

在快速代码示例中查找字符串中的字符的许多解决方案使用一种技术将较大块中的字符串读入寄存器,然后分析寄存器字节以进行匹配。当字符是单字节时,这可以正常工作,但当字符是16位unicode时,它不是最佳的。

有些示例甚至使用查找表,但在unicode字符串搜索中也不是最佳选择。

我发现fastcode purepascal PosEx_Sha_Pas_2字符串搜索例程在32/64位模式下工作得非常好,即使对于单字符搜索也是如此。 你也可以使用那个例程。

我将PosEx_Sha_Pas_2中不需要的部分删除到CharPosEx_LU_Pas中,并获得了一定的执行时间百分比:

function CharPosEx_LU_Pas(c: Char; const S: string; Offset: Integer = 1): Integer;
var
  len: Integer;
  p, pStart, pStop: PChar;
label
  Loop0, Loop4,
  TestT, Test0, Test1, Test2, Test3, Test4,
  AfterTestT, AfterTest0,
  Ret;
begin;
  p := Pointer(S);

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

  len := PLongInt(PByte(p) - 4)^; // <- Modified to fit 32/64 bit
  if (len < Offset) then
  begin;
    Exit(0);
  end;

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

  if p < pStop then
    goto Loop4;
  p := p - 4;
  goto Loop0;

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

Test3:
  p := p - 2;
Test1:
  p := p - 2;
TestT:
  p := p + 2;
  if p <= pStop then
    goto Ret;
  Exit(0);

Test4:
  p := p - 2;
Test2:
  p := p - 2;
Test0:
  Inc(p);
Ret:
  Result := p - pStart;
end;

我声称此代码段没有原创性,因为从PosEx_Sha_Pas_2中删除不需要的代码部分是一项简单的任务。

  

基准32位(101个字符串,最后一个字符匹配):   重复50000000次。

System.Pos:       1547 ms
PosEX_Sha_Pas_2:  1292 ms
CharPosEx:        2315 ms
CharPosEx_LU_Pas: 1103 ms
SysUtils.StrScan: 2666 ms
  

基准64位(101个字符串,最后一个字符匹配):   重复50000000次。

System.Pos:      20928 ms
PosEX_Sha_Pas_2:  1783 ms
CharPosEx:        2874 ms
CharPosEx_LU_Pas: 1728 ms
SysUtils.StrScan: 3115 ms