質問

この質問 完了ですが、少し異なる工夫が施されています。これは時期尚早な最適化であるといくつかの人が指摘していますが、これは実用性のためだけに、実用性のためだけに求めている場合には完全に正しいことです。私の問題は実際的な問題に根ざしていますが、それでもなお興味があります。


データベーススキーマ(簡単に数百のテーブル、ビューなど)を再作成するためのスクリプト(ディスクに保存されるような)を作成するためのSQLステートメントの束を作成しています。これは、文字列の連結が追加専用であることを意味します。 MSDNによると、StringBuilderは、内部バッファー(確かにchar [])を保持し、必要に応じて文字列文字をコピーし、配列を再割り当てすることで機能します。

しかし、私のコードには多くの繰り返し文字列があります(<!> quot; CREATE TABLE [<!> quot;、<!> quot; GO \ n <!> quot;など)。それらの利点抑留されているが、StringBuilderを使用する場合はコピーされるため、毎回。唯一の変数は基本的にテーブル名であり、すでにメモリ内にある他のオブジェクトに文字列としてすでに存在します。

データが読み込まれ、スキーマ情報を保持するオブジェクトが作成された後、すべての文字列情報をインターンで再利用できると言えますか?

仮に、文字列のListまたはLinkedListは、インターンされた文字列へのポインタを保持しているため、高速ではないでしょうか?次に、正確な長さである文字列全体の単一メモリ割り当てのためのString.Concat()の呼び出しは1回だけです。

リストはインターンされたポインターのstring []を再割り当てする必要があり、リンクされたリストはノードを作成してポインターを変更する必要があるため、<!> quot; free <!> quotではありません。しかし、私が何千ものインターンされた文字列を連結している場合、それらはより効率的であるように見えます。

これで、各SQLステートメントの文字数についてのヒューリスティックを思いつくことができると思います<!> amp;各タイプをカウントし、大まかなアイデアを取得し、StringBuilderの容量を事前に設定してchar []の再割り当てを回避しますが、再割り当ての可能性を減らすためにかなり余裕を持ってオーバーシュートする必要があります。

したがって、この場合、単一の連結文字列を取得するのが最も速いでしょう:

     
  • StringBuilder
  •  
  • リスト<!> lt; string <!> gt;インターンされた文字列の
  •  
  • LinkedList <!> lt; string <!> gt;インターンされた文字列の
  •  
  • 容量ヒューリスティックを備えたStringBuilder
  •  
  • 他に何かありますか

上記の別の質問(いつもディスクに行くとは限りません)として:出力ファイルへの単一のStreamWriterはまだ高速ですか?または、リストまたはLinkedListを使用して、メモリ内で最初に連結する代わりに、リストからファイルに書き込みます。

編集: 要求に応じて、参照(.NET 3.5) MSDNへ。 <!> quot;空きがある場合、バッファの最後に新しいデータが追加されます。そうでない場合は、新しい大きなバッファーが割り当てられ、元のバッファーのデータが新しいバッファーにコピーされ、新しいデータが新しいバッファーに追加されます。<!> quot; それを大きくするために再割り当てされ(古いデータをサイズ変更された配列にコピーする必要があります)、追加します。

役に立ちましたか?

解決

個別の質問について、Win32には WriteFileGather 関数。これは(インターンされた)文字列のリストをディスクに効率的に書き込むことができますが、ディスクが非常に大きな連結を除くすべてを覆います。

主な質問:メガバイトのスクリプトまたは数万のスクリプトに到達している場合を除き、心配しないでください。

StringBuilderは、再割り当てごとに割り当てサイズを2倍にすることができます。つまり、バッファを256バイトから1MBに増やすと、再割り当てはわずか12になります。最初の推定値がターゲットから3桁外れていることを考えると、非常に良いことです。

純粋に演習として、いくつかの見積もり:1MBのバッファーを構築すると、おおよそ3 MBのメモリ(1MBソース、1MBターゲット、1MB 再取得中のコピー)。

リンクリストの実装では、約2MBがスイープされます(文字列参照ごとの8バイト/オブジェクトオーバーヘッドは無視されます)。したがって、10Gbit / sおよび1MB L2キャッシュの一般的なメモリ帯域幅と比較して、1 MBのメモリ読み取り/書き込みを節約できます。

はい、リストの実装は潜在的に高速であり、バッファが一桁大きい場合、違いは問題になります。

小さな文字列のはるかに一般的なケースでは、アルゴリズムのゲインは無視でき、他の要因によって簡単に相殺されます。StringBuilderコードはすでにコードキャッシュにあり、マイクロ最適化の実行可能なターゲットです。また、内部で文字列を使用すると、最終的な文字列が初期バッファに収まる場合はコピーがまったく行われません。

リンクされたリストを使用すると、O(文字数)からO(セグメント数)に再割り当ての問題が発生します。文字列参照のリストは文字列と同じ問題に直面します!


そのため、IMO StringBuilderの実装は正しい選択であり、一般的なケースに最適化されており、予想外に大きなターゲットバッファーの場合はほとんど低下します。リスト実装は、最初に非常に多くの小さなセグメントで低下することが予想されます。これは、実際にはStringBuilderが最適化しようとしている極端なシナリオです。

それでも、2つのアイデアの比較を見て、リストがより速くなり始めるのは興味深いでしょう。

他のヒント

このようなものを実装している場合、StringBuilder(またはスクリプトのメモリバッファーにあるその他のもの)を作成することはありません。 代わりにファイルにストリーミングして、すべての文字列をインラインにします。

擬似コードの例は次のとおりです(構文的には正しくありません):

FileStream f = new FileStream("yourscript.sql");
foreach (Table t in myTables)
{
    f.write("CREATE TABLE [");
    f.write(t.ToString());
    f.write("]");
    ....
}

そして、文字列をすべてコピーするスクリプトのメモリ内表現は必要ありません。

意見?

私の経験では、StringBuilderは適切に割り当てられ、大量の文字列データに対して他のほとんどすべてよりも優れています。再割り当てを防ぐために推定値を20%または30%オーバーシュートしても、メモリを浪費する価値があります。現在、自分のデータを使用してバックアップするのに難しい数字はありませんが、詳細についてはこのページをご覧ください。

しかし、ジェフは指摘するのが好きなので、時期尚早に最適化しないでください!

編集:@Colin Burnettが指摘したように、Jeffが実施したテストはBrianのテストと一致しませんが、Jeffの投稿をリンクするポイントは一般的に時期尚早な最適化に関するものでした。ジェフのページのいくつかのコメント者は、彼のテストの問題を指摘しました。

実際には、StringBuilderは内部でStringのインスタンスを使用します。 Systemは実際には"SOMESTRINGA"アセンブリ内で変更可能であるため、"SOMESTRINGB"をその上に構築できます。インスタンスを作成するときに適切な長さを割り当てることで、<=>を少しだけ効果的にすることができます。そうすれば、サイズ変更操作の数を排除/削減できます。

文字列インターンは、コンパイル時に識別できる文字列に対して機能します。したがって、実行中に多くの文字列を生成する場合、文字列でインターンメソッドを呼び出して自分でそうしない限り、それらはインターンされません。

インターンは、文字列が同じ場合にのみ役立ちます。ほとんど同じ文字列はインターンの恩恵を受けないため、<=>と<=>はインターンされても2つの異なる文字列になります。

連結されるすべての(またはほとんどの)文字列がインターンされると、スキームMIGHTによりパフォーマンスが向上します。

ただし、実際にパフォーマンスが向上するかどうかは、アルゴリズムの規模ではなく一定の要因で改善されるため、処理しているデータの量に依存します。

実際に伝える唯一の方法は、両方の方法を使用してアプリを実行し、結果を測定することです。ただし、メモリに大きな負荷がかかり、バイトを節約する方法が必要でない限り、気にせず、単に文字列ビルダーを使用します。

A StringBuilderはデータの保存にchar[]を使用せず、内部の可変文字列を使用します。つまり、文字列のリストを連結するときのように最終的な文字列を作成するための追加のステップはなく、<=>は通常の文字列として内部文字列バッファを返します。

<=>が容量を増やすために行う再割り当ては、データが平均でさらに1.33回コピーされることを意味します。 <=>を作成するときにサイズの適切な見積もりを提供できる場合は、さらにそれを減らすことができます。

ただし、少し見通しを得るには、最適化しようとしているものを見てください。プログラムでほとんどの時間を要するのは、実際にデータをディスクに書き込むことです。したがって、文字列の処理を<=>を使用するよりも2倍速く最適化できる場合でも(これはほとんどありません)、全体的な違いはまだ数パーセントです。

このためにC ++を検討しましたか?できればC ++で記述されたT / SQL式を既にビルドしているライブラリクラスがあります。

文字列に関して最も遅いのはmallocです。 32ビットプラットフォームでは、文字列ごとに4 KBかかります。作成される文字列オブジェクトの数を最適化することを検討してください。

C#を使用する必要がある場合は、次のようなものをお勧めします。

string varString1 = tableName;
string varString2 = tableName;

StringBuilder sb1 = new StringBuilder("const expression");
sb1.Append(varString1);

StringBuilder sb2 = new StringBuilder("const expression");
sb2.Append(varString2);

string resultingString = sb1.ToString() + sb2.ToString();

perfが重要な場合、依存関係注入フレームワークを使用したオブジェクトのインスタンス化の最適なパスをコンピューターに評価させることさえできます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top