3

これは、連結の観点よりもパフォーマンスが高く、パフォーマンスもchar[]優れているStringBuilderと言われています。StringBuilderstring

StringBuilder私のテストでは、ループの使用とstring内部に大きな違いはありません。実際、これchar[]が最も遅いです。

44列と130,000行の同じテーブルに対してテストしています。クエリはselect*from test

誰かが私が何か間違ったことをしたかどうかを確認するのを手伝ってもらえますか?

以下はコードです

//fetchByString(rd, fldCnt, delimiter, sw);            // duration: 3 seconds

//fetchByBuilder(rd, fldCnt, delimiter, sw, rsize);    // duration: 3 seconds

//fetchByCharArray(rd, fldCnt, delimiter, sw, rsize);  // duration: 7 seconds

private void fetchByString(OracleDataReader pReader, int pFldCnt, string pDelimiter, StreamWriter pWriter)
{
  while (pReader.Read())
  {
    string[] s = new string[pFldCnt];
    for (Int32 j = 0; j < pFldCnt; j++)
    {
      if (pReader.IsDBNull(j))
      {
        s[j] = "";
      }
      else
      {
        s[j] = pReader.GetValue(j).ToString();          // correct value
      }
    }
    pWriter.WriteLine(string.Join(pDelimiter, s));      
  }
}
private void fetchByBuilder(OracleDataReader pReader, int pFldCnt, string pDelimiter, StreamWriter pWriter, int pRowSzie)
{
  StringBuilder sb = new StringBuilder(pRowSzie);
  while (pReader.Read())
  {
    for (Int32 j = 0; j < pFldCnt; j++)
    {
      if (pReader.IsDBNull(j))
      {
        //sb.Append("");
        sb.Append(pDelimiter);
      }
      else
      {
        sb.Append(pReader.GetValue(j).ToString());          // correct value
        sb.Append(pDelimiter);
      }
    }
    pWriter.WriteLine(sb.ToString());
    sb.Clear();
  }
}
private void fetchByCharArray(OracleDataReader pReader, int pFldCnt, string pDelimiter, StreamWriter pWriter, int pRowSzie)
{
  char[] rowArray;
  int sofar; 
  while (pReader.Read())
  {
    rowArray = new char[pRowSzie];
    sofar = 0;
    for (Int32 j = 0; j < pFldCnt; j++)
    {
      if (pReader.IsDBNull(j))
      {
        pDelimiter.CopyTo(0, rowArray, sofar, pDelimiter.Length);
        sofar += pDelimiter.Length;
      }
      else
      {
        pReader.GetValue(j).ToString().CopyTo(0, rowArray, sofar, pReader.GetValue(j).ToString().Length);
        sofar += pReader.GetValue(j).ToString().Length;
        pDelimiter.CopyTo(0, rowArray, sofar, pDelimiter.Length);
        sofar += pDelimiter.Length;
      }
    }
    string a = new string(rowArray).TrimEnd('\0');
    pWriter.WriteLine(a);
  }
}
4

2 に答える 2

5

文字列concatは、データの一時的な中間コピーを各+演算子で頻繁に割り当てる必要があるため、StringBuilderが文字列concatよりも優先されます。これにより、大量のメモリが高速で消費され、データを複数回コピーする必要があります。StringBuilder.Append()は、サブセグメントを複数回コピーまたは割り当てないように内部的に最適化されています。すべての作業はStringBuilder.ToStringで行われ、出力文字列の最終的なサイズがわかっていて、1回の呼び出しで割り当てることができます。

テストケースは文字列連結を使用していません。一連の文字列フラグメントを文字列の配列に割り当ててから、String.Joinを呼び出します。これは基本的に、StringBuilderが内部で行うことです。ベンチマーク時間を支配している可能性のあるデータI/Oのオーバーヘッドを取り除いた後でも、String.Join()とStringBuilder.ToString()が同様のパフォーマンスを発揮することを期待しています。

于 2012-11-02T17:33:58.980 に答える
3

私はこの主張に精通していませんが、char[]あなたがそれを書いた方法で、はるかに多くの変換が行われているようです。

pReader.GetValue().ToString()string、(の代わりに)作業しているものとは異なる形式で値を配置することに加えて、他の1つだけではなく、割り当てchar[]で3回発生しています。char[]おそらく、「真の値」をaに直接キャストしてchar[]有効にする方法を見つける必要があります。そうでなければ、ベンチマークの観点から、理論的には、他の何かからの速度低下を導入することによってパフォーマンスを低下させる可能性があります。私はそれが起こっていると断言していませんが、手続き的にはそれが重要であると考えられています。それができない場合でも、関連するGetValue / ToString呼び出しの代わりに入力しvar stringRep = pReader.GetValue().ToString()て使用すると、パフォーマンスがわずかに向上する可能性があると思います。stringRep

ちなみに、これをどのようにタイミングをとっているのかはわかりませんが、Stopwatchクラスを使用していない場合は、タイミングが適切であることを確認するために、それを調べることができます。これは基本的に、この種のベンチマークを念頭に置いて作成されています。これにより、オラクルリーダーからすべての混乱を取得することなく、ベンチマークしようとしているもの(連結操作)を実際に分離することもできます。

于 2012-11-02T17:24:37.757 に答える