【问题标题】:Replacing string assignment by PChar operation用 PChar 操作替换字符串赋值
【发布时间】:2016-02-29 09:59:47
【问题描述】:

我有一个难以理解的令人费解的结果。

我一直在尝试提高这个例程的速度

function TStringRecord.GetWord: String;
begin
  // return the next word in Input
  Result := '';

  while (PC^ <> #$00) and not PC^.IsLetter do begin
    inc(FPC);
  end;

  while (PC^ <> #$00) and PC^.IsLetter do begin
    Result := Result + PC^;
    inc(FPC);
  end;
end;

Result := Result + PC^ 替换为基于指针的操作。这 是我的尝试:

function TStringRecord.GetWord2: String;
var
  Len : Integer;
  StartPC,
  DestPC : PChar;
begin
  // return the next word in Input
  Result := '';

  while (PC^ <> #$00) and not PC^.IsLetter do begin
    inc(FPC);
  end;

  Len := Length(Input);
  SetLength(Result, Len);
  StartPC := PChar(Result);
  DestPC := PChar(Result);
  while (PC^ <> #$00) and PC^.IsLetter do begin
    WStrPLCopy(DestPC, PC, 1);
    inc(FPC);
    inc(DestPC);
  end;
  SetLength(Result, DestPC - StartPC);
end;

根据我的线路分析器,WStrPLCopy(DestPC, PC, 1) 需要 50 倍的时间 比Result := Result + PC^。据我所知,这是因为在进入 对 WStrPLCopy 有一个对 _WStrFromPWChar 的调用,这似乎复制了更多 字符比必要的字符。我怎样才能避免这种情况,或者有人可以建议 另一种基于 PChar 的方法?

我的代码的其余部分如下:

TStringRecord = record
private
  FPC: PChar;
  FInput: String;
  procedure SetInput(const Value: String);
public
  function NextWord : String;
  function NextWord2 : String;
  property Input : String read FInput write SetInput;
  property PC : PChar read FPC;
end;

procedure TStringRecord.SetInput(const Value: String);
begin
  FInput := Value;
  FPC := PChar(Input);
end;

【问题讨论】:

  • 如果没有您向我们展示有意义的代码,就很难提供帮助。您省略了对输入字符串的任何引用。当一个就足够时,您正在执行两个堆分配。你在抄袭。当然你需要找到开始和结束索引,并使用Copy。我向你保证,如果你展示你的代码,你会得到更好的答案。
  • 我无意隐瞒任何事情,只是想省略不相关的细节。输入通常可以是几个 k 个字符。正如您所说,我在复制什么“零碎”,您对两个堆分配有什么想法?
  • 您执行了两次堆分配。它们在代码中尽可能简单。你只需要做一个。而不是一个字符一个字符地复制,我会在最后复制一次。我不会在这里使用PChar,而是使用索引。我会非常警惕线轮廓仪。使用秒表计时。
  • 无论如何,我都不愿意写任何代码,因为仍然有很多缺失的信息。我们不知道典型的输入数据,代码如何与您的程序的其余部分相适应,等等。
  • "代码如何适应" 哦,这只是一个自学练习,典型的输入是任何东西,真的。无论如何,我不打算要求任何代码。我更改了我的代码以大致按照您的建议进行(仅在找到结尾后复制)并且它工作正常并且在速度方面与 GetWord 方法相当,所以谢谢。不确定我的 q 应该得到 -1 的结果,但我猜这就是 SO 的乐趣所在。我仍然对调用 _WStrFromPWChar 的内容感到好奇,但我想我会单独询问这个问题,以免因我的代码不赞成而陷入困境。

标签: delphi delphi-10-seattle


【解决方案1】:

我会这样写:

function TStringRecord.GetWord: String;
var beg: PChar;
begin
  // return the next word in Input
  while (FPC^ <> #0) and not FPC^.IsLetter do 
    inc(FPC);
  beg := FPC;
  while (FPC^ <> #0) and FPC^.IsLetter do 
    inc(FPC);
  SetString(result, beg, FPC-beg);
end;

有了这个,代码可读性很强,而且你有一个单一的内存分配,我猜你不能更快地写任何东西(但是通过内联PC^.IsLetter,这是对外部代码的唯一调用)。

【讨论】:

  • 假设第一次调用,只返回Input 开头的最终空格(不是 PC^.IsLetter)。如果你解决了这个问题,你仍然需要在设置 beg 之前在单词之间推进过去的空格
  • @TomBrunberg 你说得对:我没有完全遵循所使用的搜索算法 - 只是想展示如何使用 PChar 变量对一些字符串提取进行编码,并使用单个内存分配。
  • 使用[] 索引不是比基于指针算法的方法更快吗?
  • @DavidHeffernan 两者都编译为相同的 asm,但是当您使用调用 UniqueString() AFAIR 的 @astring[20] 时。在这个 OP 的上下文中,输入使用 PChar,所以我的答案使用 PChar。当然,普通的 Delphi 代码可能会使用一些 index + copy(),但性能相同。
  • @DavidHeffernan 或当您编写 @astring[20] 以检索指向字符 AFAIR 的指针时,即使您不打算修改其内容。在某些情况下,指针可能比索引字符串访问稍快,因为它可以避免每次访问使用两个寄存器:它可能生成更好的代码,特别是如果你的函数很短,并且将指针作为其输入参数之一。
猜你喜欢
  • 1970-01-01
  • 2021-09-26
  • 1970-01-01
  • 2021-06-14
  • 2013-05-04
  • 2010-09-10
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
相关资源
最近更新 更多