【发布时间】:2015-06-14 15:57:44
【问题描述】:
在分析过程中,我遇到了一个需要花费大量时间的函数,但基本上可以归结为这段非常简单的代码:
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 一样从一开始),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 版本)中并没有太大不同。
【问题讨论】:
-
你也可以尝试内联
GetSubString()。 -
“除了 SetString 工作从零开始(不是从一作为复制)” -
SetString()没有索引,句号。它对起始指针而不是索引进行操作。Copy()有一个从 1 开始的索引参数,因此原来的GetSubstring()也有一个从 1 开始的AStart。您最初修改的GetSubstring()正在计算错误的指针。它需要使用-1而不是+1来保持与Copy()相同的语义(不包括极端情况):SetString(Result, Ainput + (AStart - 1), ASubstringLength); -
@RemyLebeau 你是对的。我过度简化了
Copy和SetString之间的区别。此外,由于我无法访问我的工作机器,我不得不从内存中复制代码,这导致 +1 而不是 -1 和SetString函数/过程混淆。现在修复了。 -
@LURD 有趣的是,在内联该方法后,它所花费的时间大约是非内联版本的两倍。不知道为什么会这样,以及它是否与我测量它的方式有关;微基准测试充满了问题。但是,不扫描整个字符串并每次都复制它就足以提高性能。
标签: string delphi optimization pchar