在分析过程中,我遇到了一个函数,该函数花了很多时间,但实际上归结为这段非常简单的代码:
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版本)没有太大不同。
让我们考虑一些极端情况。我认为他们是:
AInput
无效。AStart < 1
。AStart > FLength
。ASubstringLength < 0
。ASubstringLength + (AStart-1) > FLength
。我认为我们可以忽略情况1。责任应由呼叫者提供有效PChar
。的确AInput <> nil
,我认为您的支票已经nil
无效,因为这是无效的PChar
。
在剩下的部分中,您介绍了2和5,但没有介绍3和4。因此,如果用户提供的值AStart
太大,那么您将读取字符串的末尾。同样,用户可以轻松提供负片ASubstringLength
。我认为您显然很称职,因此您不需要任何人来编写代码来检查这些情况。
现在,如果您真的在乎性能的每一个下降,那么您就不应该检查这些情况。要求用户传递有效参数。在调试模式下,使用{$IFOPF D+}
或Assert
可以检查输入。当然,如果这些论点来自外部,则应该对其进行验证。
另一方面,原始代码遭受的最大性能损失是整个字符串的不必要扫描,以及复制到中间堆分配的字符串。一旦删除了这些,就大大减少了进一步提高性能的机会。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句