浅いC〓〓StrigBuiderのメモリのかけらの性能に対する影響を話します。


StringBuiderの内部はマルチセグメントのchar[]からなる半自動チェーンであるため、頻繁に中間からStringBuiderを修正すると、元の連続メモリを複数のセグメントに分離して読み取り/エルゴード性能に影響を与えます。
連続メモリと不連続メモリの性能差は、1600倍に達する可能性があります。
背景
StringBuiderを使うユーザーの多くは、SteringBuilderでつなぎ合わせたいと思っているかもしれません。しかし、いくつかの特殊なシーンでは、プログラミング言語のために言語サービスを提供したり、リッチテキストエディタを書いたりする時には、StringBuiderは相変わらず有効な場所があります。中のInsert/Removeの2つの方法で修正します。
テスト方法
Talk is chap,show me the code:

int docLength = 10000;
void Main()
{
  (from power in Enumerable.Range (1, 16)
  let mutations = (int) Math.Pow (2, power)
  select new
  {
    mutations,
    PerformanceRatio = Math.Round (GetPerformanceRatio (docLength, mutations), 1)
  }).Dump();
}

float GetPerformanceRatio (int docLength, int mutations)
{
  var sb = new StringBuilder ("".PadRight (docLength));
  var before = GetPerformance (sb);
  FragmentStringBuilder (sb, mutations);
  var after = GetPerformance (sb);
  return (float) after.Ticks / before.Ticks;
}

void FragmentStringBuilder (StringBuilder sb, int mutations)
{
  var r = new Random(42);
  for (int i = 0; i < mutations; i++)
  {
    sb.Insert (r.Next (sb.Length), 'x');
    sb.Remove (r.Next (sb.Length), 1);
  }
}

TimeSpan GetPerformance (StringBuilder sb)
{
  var sw = Stopwatch.StartNew();
  long tot = 0;
  for (int i = 0; i < sb.Length; i++)
  {
    char c = sb[i];
    tot += (int) c;
  }
  sw.Stop();
  return sw.Elapsed;
}

このコードについては、以下の点に注意してください。
  • .PadRight(n)によって直接に長さnの空白文字列を作成し、new string(',n)で代替することができる。
  • new Random(42)において、ランダム因子を指定しました。分離後の位置が完全に同じであることを確認して、対照グループを作るのに有利です。
  • 文字列を2^1~2^16回修正しました。それぞれ何度も修正した後の性能差を比較しました。
  • 私はsb[i]を使ってStringBuiderの中の位置を一つ一つ訪問し、メモリの不連続性をより際立たせます。
  • 実行結果
    muttions
    PerformancRatio
    2
    1
    4
    1
    8
    1
    16
    1
    32
    1
    64
    1.1
    128
    1.2
    256
    1.8
    512
    5.2
    1024
    19.9
    2048
    81.3
    4096
    274.5
    8192
    745.8
    16384
    158.8
    32768
    1630.4
    65536
    930.8
    StringBuilderの間で大量に修正すれば、その性能は急降下し、32768回の修正を見て、遍歴は1630.4倍の性能差が生じることが分かります。
    解決策
    もしStringBuilderを使用しなければならないならば、一定回数を修正した後、新たにStrigBuilderを作成して、アクセス時に最適なメモリの連続性を得ることができると考えられます。
    
    void FragmentStringBuilder (StringBuilder sb, int mutations)
    {
      var r = new Random(42);
      for (int i = 0; i < mutations; i++)
      {
        sb.Insert (r.Next (sb.Length), 'x');
        sb.Remove (r.Next (sb.Length), 1);
        
        //   
        const int defragmentCount = 250;
        if (i % defragmentCount == defragmentCount - 1)
        {
          string buf = sb.ToString();
          sb.Clear();
          sb.Append(buf);
        }
      }
    }
    
    以上のように250回の修正を経て、元のSteringBuiderを削除し、新たにStrigBuilderを作成します。この時の運転効果は以下の通りです。
    muttions
    PerformancRatio
    2
    1.2
    4
    0.7
    8
    1
    16
    1
    32
    1
    64
    1.1
    128
    1.2
    256
    1
    512
    1
    1024
    1
    2048
    1
    4096
    1.1
    8192
    1.5
    16384
    1.3
    32768
    1
    65536
    1
    ほぼすべての場合、メモリ不連続によるアクセス性能の問題を解決することができます。同時に250は比較的合理的な数字かもしれません。挿入性能と照会/エルゴード性能の中でバランスが取れています。
    反省と総括
    ご存知のように、stringの不変性のため、多くの文字列をつなぎ合わせる時、大量のメモリを浪費します。しかし、StringBuiderを使用するには、その構造を理解する必要があります。
    StringBuiderのようにチェーン式の構造を作るのは原因がないわけではないです。挿入性能を考えるとチェーン式インターフェースが一番優れています。ただし、検索性能を考えると、チェーン構造は非常に不利であり、チェーン構造ではなく、中間から挿入する場合は、StringBuiderのメモリ空間が足りないかもしれないので、メモリを再割り当てする必要があります。
    この文章は実は非常に特殊な例です。実際には言語サービス、エディタ以外に、このような頻繁な挿入が必要で、速いシーンも頻繁に修正します。簡単にしたいなら、SteringBuiderを使うのが条件付きの解決策です。より適切な解決策はもちろん、専門的なデータ構造である。PieceTableは、マイクロソフトがVCodeエディタで、大きなファイルの編集性能を確保するために、このデータ構造を使用して、非常に良い結果を得た。
    ここでは、StringBuiderメモリの破片の性能に対する影響についての簡単な記事を紹介します。StringBuiderメモリの破片に関する詳細を紹介します。以前の記事を検索してください。または、下記の関連記事を引き続きご覧ください。これからもよろしくお願いします。