6

標準の文字列の追加が非常に遅いことがわかったので、コードを高速化できるヒント/ハックを探しています。

私のコードは基本的に次のように構成されています。

inline void add_to_string(string data, string &added_data) {
   if(added_data.length()<1) added_data = added_data + "{";
   added_data = added_data+data;
}

int main()
{
   int some_int = 100;
   float some_float = 100.0;
   string some_string = "test";

   string added_data;
   added_data.reserve(1000*64);

   for(int ii=0;ii<1000;ii++)
   {
      //variables manipulated here
      some_int = ii;  
      some_float += ii;
      some_string.assign(ii%20,'A');
      //then we concatenate the strings!
      stringstream fragment;
      fragment<<some_int <<","<<some_float<<","<<some_string;
      add_to_string(fragment.str(),added_data);
   }
   return;
}

基本的なプロファイリングを行っていると、for ループで大量の時間が使用されていることがわかりました。これを大幅にスピードアップするためにできることはありますか? c++ 文字列の代わりに c 文字列を使用すると役立つでしょうか?

4

7 に答える 7

5

文字列の追加は、あなたが直面している問題ではありません。std::stringstream は、その設計により遅いことが知られています。for ループの反復ごとに、stringstream は少なくとも 2 回の割り当てと 2 回の削除を担当します。これら 4 つの操作のそれぞれのコストは、文字列の追加よりも多くなる可能性があります。

以下をプロファイリングし、違いを測定します。

std::string stringBuffer;
for(int ii=0;ii<1000;ii++)
{
  //variables manipulated here
  some_int = ii;  
  some_float += ii;
  some_string.assign(ii%20,'A');
  //then we concatenate the strings!
  char buffer[128];
  sprintf(buffer, "%i,%f,%s",some_int,some_float,some_string.c_str());
  stringBuffer = buffer;
  add_to_string(stringBuffer ,added_data);
}

理想的には、sprintf を _snprintf またはコンパイラでサポートされている同等のものに置き換えます。

経験則として、デフォルトでは書式設定に stringstream を使用し、パフォーマンスが重要な場合は常に sprintf、itoa などの高速で安全性の低い関数に切り替えます。

編集:それ、そしてディディエルクが言ったこと: added_data += data;

于 2012-12-13T01:17:10.890 に答える
4

add_to_stringループで呼び出さないと、多くの文字列操作を節約できます。

私はこれが同じことをすると信じています (私は C++ の専門家ではなく、何をするのか正確にはわかりませんstringstreamが):

stringstream fragment;
for(int ii=0;ii<1000;ii++)
{
  //variables manipulated here
  some_int = ii;  
  some_float += ii;
  some_string.assign(ii%20,'A');
  //then we concatenate the strings!
   fragment<<some_int<<","<<some_float<<","<<some_string;
}

// inlined add_to_string call without the if-statement ;)
added_data = "{" + fragment.str();
于 2012-12-13T01:15:05.663 に答える
3

でreserveメソッドを使用したようですadded_data。これは、文字列が大きくなるにつれて文字列の複数の再割り当てを回避するのに役立つはずです。

+=可能な場合は、文字列演算子も使用する必要があります。

added_data += data;

added_data上記は、カテネーションを行うときに一時的な文字列で不必要なコピーを行ったり来たりしないようにすることで、かなりの時間を節約できると思います。

この+=演算子はメソッドのより単純なバージョンであり、の最後に直接string::appendコピーするだけです。リザーブを作成したので、その操作だけでも非常に高速になります(strcpyとほぼ同等)。dataadded_data

しかし、すでに文字列ストリームを使用して入力を処理しているのに、なぜこれらすべてを実行するのでしょうか。そもそもすべてをそこに置いておきましょう!

stringstreamクラスは確かにあまり効率的ではありません。

必要に応じて、文字列ストリームクラスの使用方法の詳細を確認できますが、文字列をバッファとして使用するソリューションでは、クラス速度の問題を回避できるようです。

いずれにせよ、自分が何をしているのかを本当に理解していない限り、純粋なCで速度が重要なコードを再実装しようとする試みには近づかないでください。他のいくつかのSO投稿はそれを行うという考えをサポートしていますが、時間の経過とともに拡張される標準ライブラリに可能な限り依存し、あなた(または私)考えないでしょう。入力データ形式が石に設定されている場合は、その道を進むことを考え始めるかもしれませんが、そうでなければ、それは時期尚早の最適化です。

于 2012-12-13T01:27:12.023 に答える
2

LLVMで使われているTwineを見てください。

Twine は一種のロープで、バイナリ ツリーを使用して連結された文字列を表します。ここで、文字列はノードの順序です。結果が使用されるときに Twine をバッファに効率的にレンダリングできるため、特に Twine の結果がまったく必要ない場合に、中間の文字列結果の一時的な値を生成するコストを回避できます。リーフ ノードのタイプを明示的に追跡することで、変換操作 (文字列に整数を追加するなど) のための一時的な文字列の作成を回避することもできます。

問題の解決に役立つ場合があります。

于 2012-12-13T01:44:21.500 に答える
2

で開始added_dataすると、メソッドから"{"を削除できます。文字列が空の場合、 は 1 回だけ実行されるため、すぐに空でないようにすることもできます。ifadd_to_stringif

さらに、あなたadd_to_stringdata;のコピーを作成します。変更されないため、これは必要ありません。data参照によって受け入れると、const物事がスピードアップするはずです。

最後に、added_datafromstringを toに変更すると、ループの反復ごとに作成、コピー、および破棄さsstreamれる仲介者なしで、ループ内でそれに追加できるようになります。sstream

于 2012-12-13T01:10:51.097 に答える
0

このアプローチはどうですか?

これは、MSVC 2010 レポートの DevPartner です。

ここに画像の説明を入力

于 2012-12-13T02:07:03.350 に答える
-4

文字列 newstring = stringA & stringB;

文字列が遅いとは思いません。変換が遅くなる可能性があり、コンパイラが変数の型の不一致をチェックする可能性があります。

于 2012-12-13T01:10:41.597 に答える