我试图改进一个解析文本文件(1.5GB)的旧例程。该例程非常愚蠢,它正在构建这样的字符串:Result:= Result+ buff[i];
因此,我认为 TStringBuilder 将显着提高速度。事实证明,“哑”代码实际上比使用 TStringBuilder 的“改进”版本快 114%。
因此,从字符构建字符串并不是一个可以使用 TStringBuilder 提高速度的地方。
我的 StringBuilder(如下)比经典的 s:= s+ chr快 184.82 倍(是的 184 !!!!!!) 。(4MB 字符串的实验)
经典 s:= s + c
时间:8502 ms
procedure TfrmTester.btnClassicClick(Sender: TObject);
VAR
s: string;
FileBody: string;
c: Cardinal;
i: Integer;
begin
FileBody:= ReadFile(File4MB);
c:= GetTickCount;
for i:= 1 to Length(FileBody) DO
s:= s+ FileBody[i];
Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms'); // 8502 ms
end;
预缓冲
Time:
BuffSize= 10000; // 10k buffer = 406ms
BuffSize= 100000; // 100k buffer = 140ms
BuffSize= 1000000; // 1M buffer = 46ms
代码:
procedure TfrmTester.btnBufferedClick(Sender: TObject);
VAR
s: string;
FileBody: string;
c: Cardinal;
CurBuffLen, marker, i: Integer;
begin
FileBody:= ReadFile(File4MB);
c:= GetTickCount;
marker:= 1;
CurBuffLen:= 0;
for i:= 1 to Length(FileBody) DO
begin
if i > CurBuffLen then
begin
SetLength(s, CurBuffLen+ BuffSize);
CurBuffLen:= Length(s)
end;
s[marker]:= FileBody[i];
Inc(marker);
end;
SetLength(s, marker-1); { Cut down the prealocated buffer that we haven't used }
Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
if s <> FileBody
then Log.Lines.Add('FAILED!');
end;
预缓冲,作为类
Time:
BuffSize= 10000; // 10k buffer = 437ms
BuffSize= 100000; // 100k buffer = 187ms
BuffSize= 1000000; // 1M buffer = 78ms
代码:
procedure TfrmTester.btnBuffClassClick(Sender: TObject);
VAR
StringBuff: TCStringBuff;
s: string;
FileBody: string;
c: Cardinal;
i: Integer;
begin
FileBody:= ReadFile(File4MB);
c:= GetTickCount;
StringBuff:= TCStringBuff.Create(BuffSize);
TRY
for i:= 1 to Length(FileBody) DO
StringBuff.AddChar(filebody[i]);
s:= StringBuff.GetResult;
FINALLY
FreeAndNil(StringBuff);
END;
Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
if s <> FileBody
then Log.Lines.Add('FAILED!');
end;
这是课程:
{ TCStringBuff }
constructor TCStringBuff.Create(aBuffSize: Integer= 10000);
begin
BuffSize:= aBuffSize;
marker:= 1;
CurBuffLen:= 0;
inp:= 1;
end;
function TCStringBuff.GetResult: string;
begin
SetLength(s, marker-1); { Cut down the prealocated buffer that we haven't used }
Result:= s;
s:= ''; { Free memory }
end;
procedure TCStringBuff.AddChar(Ch: Char);
begin
if inp > CurBuffLen then
begin
SetLength(s, CurBuffLen+ BuffSize);
CurBuffLen:= Length(s)
end;
s[marker]:= Ch;
Inc(marker);
Inc(inp);
end;
结论:
如果您有大(超过 10K)字符串,请停止使用 s:= s + c 。即使您有小字符串但您经常这样做也可能是真的(例如,您有一个函数正在对小字符串进行一些字符串处理,但您经常调用它)。
_
PS:您可能还想看看这个:https ://www.delphitools.info/2013/10/30/efficient-string-building-in-delphi/2/