4

在分析过程中,我遇到了一个需要花费大量时间的函数,但基本上可以归结为这段非常简单的代码:

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  Result := Copy(AInput, AStart, ASubstringLength);
end;

此函数返回预期的子字符串,但对于较长的输入它不能很好地扩展。我在 CPU 视图中查看了汇编代码,据我所知(我通常不在汇编级别工作),似乎AInput在调用Copy.

但是由于此时字符串/字符数组的长度是未知的,因此转换代码必须遍历 的长度,PChar直到找到空终止符。这可以解释较长输入的可怕缩放。

但是,由于调用者传入了 的长度PChar,我最初认为可以将方法转换为使用SetString

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  SetString(Result, AInput + AStart - 1, ASubstringLength);
end;

除了SetString从零开始工作(不是从一开始的复制),在验证其输入方面似乎还有许多其他的小事情Copy,并非所有这些都记录在案(例如,任何小于 1 的起始值都会被更改到 1)。所以上面的幼稚实现并不总是像原来的那样工作。

我的目标是尽可能多地复制该Copy例程,因为此函数是库的一部分,并且已被我的同事广泛使用。

我想知道以下实现是否实现了这一点,或者我是否需要了解Copy. 注意:FLength实际长度AInput来自该函数所属模块的另一部分。我为这个例子删除了其他部分。

function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
  if (AInput = nil) then begin
    Result := '';
  end else begin
    if (AStart < 1) then begin
      AStart := 0;
    end else begin
      AStart := AStart - 1;
    end;
    if (ASubstringLength + AStart > FLength) then begin
      ASubstringLength := FLength - AStart;
    end;
    SetString(Result, AInput + AStart, ASubstringLength);
  end;
end;

我使用的是 Delphi 2006,但我认为这在产品的其他版本(至少非 Unicode 版本)中并没有太大的不同。

4

2 回答 2

5

让我们考虑极端情况。我认为他们是:

  1. AInput无效的。
  2. AStart < 1.
  3. AStart > FLength.
  4. ASubstringLength < 0.
  5. ASubstringLength + (AStart-1) > FLength.

在我看来,我们可以忽略案例 1。调用者有责任提供有效的PChar. 事实上,AInput <> nil在我看来,您的支票已经迈得太远了,因为nil它不是有效的PChar.

在其余部分中,您已经涵盖了 2 和 5,但没有涵盖 3 和 4。因此,如果用户提供的值AStart太大,那么您将读取字符串的末尾。同样,用户可以很容易地提供否定的ASubstringLength. 我认为您不需要任何人编写代码来检查这些情况,因为您显然非常称职。

现在,如果您真的关心每一次性能下降,那么您不应该检查任何这些情况。要求用户传递有效参数。在调试模式下,使用{$IFOPF D+}Assert您可以检查输入。当然,如果这些论点来自外部来源,那么它们应该得到验证。

另一方面,原始代码遭受的最大性能损失是对整个字符串的不必要扫描,以及复制到中间堆分配的字符串。一旦你像你一样删除了这些,那么进一步提高性能的机会就会大大减少。

于 2015-06-14T16:15:33.947 回答
0

与其转换PCharstring您,不如尝试从AInput + (AStart * SizeOf(PChar))长度为ASubstringLength * SizeOf(PChar)to的地址复制内存,@Result因为它更容易Result作为指针处理。

Move程序可以做到这一点。

于 2015-06-15T06:50:06.020 回答