15

私は1年前にプログラムをDelphi4からDelphi2009に変換しました。これは主に、Unicodeにジャンプするためだけでなく、Delphiの長年にわたる改善の恩恵を受けるためでもあります。

したがって、私のコードはもちろん、すべてレガシーコードです。短い文字列を使用しますが、現在はすべて長いUnicode文字列になっているので、古いANSI関数をすべて新しい同等の文字列に変更しました。

しかし、Delphi 2009では、おそらく.NETのStringBuilderクラスをモデルにしたTStringBuilderクラスが導入されました。

私のプログラムは多くの文字列の処理と操作を行い、数百メガバイトの大きな文字列を一度にメモリにロードして処理することができます。

DelphiのTStringBuilderの実装についてはよくわかりませんが、デフォルトの文字列操作を使用するよりも高速な操作があると聞きました。

私の質問は、努力を重ねて、標準の文字列をTStringBuilderクラスを使用するように変換する価値があるかどうかです。それを行うことで何が得られ、何が失われますか?


あなたの答えに感謝し、私の結論に私を導きます。それは、.NET互換性が必要とされない限り気にしないでください。

Jolyon Smithは、Delphi 2009 String Performanceに関するブログで、次のように述べています。

しかし、TStringBuilderは、Win32 / .NETコードベースをシングルソース化することを希望または必要とする開発者を除いて、Win32アプリケーションの開発者に実際のメリットを提供するのではなく、主に.NET互換性フィクスチャとして存在するように見えます。文字列処理のパフォーマンスは問題ではありません。

4

6 に答える 6

13

私の知る限り、TStringBuilderは、.NETおよびJavaとの同等性のために導入されたものであり、主要な進歩というよりも、チェックボックスタイプの機能のようです。

TStringBuilderは、一部の操作では高速ですが、他の操作では低速であるというのがコンセンサスのようです。

あなたのプログラムは、TStringBuilderの前後の比較を行うのに興味深いプログラムのように聞こえますが、私は学術的な演習として以外は行いません。

于 2009-10-18T19:28:58.543 に答える
11

基本的に、私はこれらのイディオムを文字列の作成に使用します。最も重要な違いは次のとおりです。

複雑なビルドパターンの場合、最初のコードはコードを非常にクリーンにし、2番目のパターンは、行を追加して多くのFormat呼び出しを含む場合にのみ使用します。

3つ目は、フォーマットパターンが重要な場合に、コードをよりクリーンにします。

最後の1つは、式が非常に単純な場合にのみ使用します。

最初の2つのイディオムの違いは次のとおりです。

  • TStringBuilderの多くのオーバーロードがAppendあり、canのような行を追加する場合は、AppendLine (2つのオーバーロードのみ)もありますTStringList.Add
  • TStringBuilder容量超過スキームを使用して基になるバッファーを再割り当てします。つまり、大きなバッファーと頻繁な追加を使用すると、よりもはるかに高速になる可能性があります。TStringList
  • コンテンツを取得するには、処理速度を低下させる可能性のあるToStringメソッドをTStringBuilder呼び出す必要があります。

つまり、文字列を追加するイディオムを選択する上で、速度は最も重要な問題ではありません。読み取り可能なコードはです。

于 2009-10-19T09:29:02.857 に答える
10

テキストファイル(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/

于 2015-08-07T12:56:23.983 に答える
8

TStringBuilderは、アプリケーションがDelphiおよびDelphi.NETで文字列処理を実行するためのソースコード互換メカニズムを提供するためにのみ導入されました。Delphi.NETの潜在的に重要な利点のために、Delphiの速度をいくらか犠牲にします。

.NETStringBuilderの概念は、そのプラットフォームでの文字列の実装に関するパフォーマンスの問題に対処します。これは、Delphi(ネイティブコード)プラットフォームにはない問題です。

ネイティブコードとDelphi.NETの両方用にコンパイルする必要のあるコードを記述していない場合は、TStringBuilderを使用する理由はありません。

于 2009-10-18T19:54:06.627 に答える
7

Marco Cantuによると、速度は向上していませんが、.Netとのコードの互換性が向上する可能性があります。ここ(およびここでのいくつかの修正)は、TStringBuilderが高速ではない別の速度テストです。

于 2009-10-18T19:28:21.957 に答える
6

LachlanGが言ったように、TStringBuilderは基本的にはあまりにも機能的です。CLR文字列は不変であるため、.NETで必要ですが、Delphiにはその問題がないため、回避策として文字列ビルダーは実際には必要ありません。

于 2009-10-18T19:52:34.290 に答える