Per*_*xus 4 delphi string optimization pchar
在分析期间,我遇到了一个耗费了相当多时间的函数,但基本上归结为这段非常简单的代码:
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
Result := Copy(AInput, AStart, ASubstringLength);
end;
Run Code Online (Sandbox Code Playgroud)
此函数返回预期的子字符串,但对于较长的输入,它不能很好地扩展.我查看了CPU视图中的汇编程序代码,从我所知道的(我通常不在汇编程序级别工作),似乎AInput
在调用之前隐式转换为字符串Copy
.
但是,由于此时字符串/字符数组的长度未知,因此转换代码必须遍历它的长度,PChar
直到找到空终止符.这可以解释较长输入的可怕缩放.
但是,由于调用者传入的长度PChar
,我最初认为可以只转换使用的方法SetString
.
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
SetString(Result, AInput + AStart - 1, ASubstringLength);
end;
Run Code Online (Sandbox Code Playgroud)
除了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;
Run Code Online (Sandbox Code Playgroud)
我正在使用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
检查输入.当然,如果这些论点来自外部来源,那么它们应该得到验证.
另一方面,原始代码遭受的最大性能损失是不必要的扫描整个字符串,并将复制到中间堆分配的字符串.一旦你删除了那些,就像你一样,那么进一步提高性能的机会就会大大减少.
归档时间: |
|
查看次数: |
790 次 |
最近记录: |