テキストファイル(1.5GB)を解析していた古いルーチンを改善しようとしました。ルーチンはかなり馬鹿げていて、次のような文字列を作成していました。Result:= Result+ buff[i];
したがって、TStringBuilderによって速度が大幅に向上します。「ダム」コードは、TStringBuilderを使用した「改良」バージョンよりも実際には114%高速であることが判明しました。
したがって、文字から文字列を作成することは、TStringBuilderで速度を向上させることができる場所ではありません。
私のStringBuilder(下記)は、従来のs:= s + chrよりも184.82倍高速です(はい184 !!!!!!) 。(4MBの文字列での実験)
クラシックs:= s + c
時間:8502ミリ秒
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/