12

LOHフラグメンテーションを発生させずに、大きな文字列で(一致を見つけるために)多くの正規表現を実行するにはどうすればよいですか?

これStringBuilderは.NETFramework4.0なので、使用しているのでLOHには含まれていませんが、RegExを実行する必要があるとすぐに呼び出す必要がありStringBuilder.ToString()ます。つまり、LOHに含まれることになります。

この問題の解決策はありますか?このような大きな文字列や正規表現を処理する長時間実行アプリケーションを使用することは事実上不可能です。

この問題を解決するためのアイデア:

この問題について考えているうちに、私は汚い解決策を見つけたと思います。

ある時点で私は5つの文字列しか持っておらず、これらの5つの文字列(85KBより大きい)はに渡されRegEx.Matchます。

新しいオブジェクトがLOHの空のスペースに収まらないために断片化が発生するため、これで問題が解決するはずです。

  1. PadRightすべての文字列を最大にします。許容サイズ、たとえば1024KB(これを行う必要がある場合がありますStringBuider
  2. そうすることで、前の文字列はすでにスコープ外にあるため、すべての新しい文字列はすでに空になったメモリに収まります
  3. オブジェクトのサイズは常に同じであるため、断片化は発生しません。したがって、特定の時間に1024 * 5のみを割り当て、LOHのこれらのスペースはこれらの文字列間で共有されます。

この設計の最大の問題は、他の大きなオブジェクトがLOHでこの場所を割り当てると、アプリケーションが1024 KBの文字列を大量に割り当て、さらに悪い断片化が発生する場合にどうなるかを推測します。fixedステートメントは役立つかもしれませんが、固定メモリアドレスにない新しい文字列を実際に作成せずに、固定文字列をRegExに送信するにはどうすればよいですか?

この理論について何か考えはありますか?(残念ながら、問題を簡単に再現することはできません。通常、メモリプロファイラーを使用して変更を観察しようとしていますが、このためにどのような分離されたテストケースを作成できるかわかりません)

4

3 に答える 3

7

OK、これが私の試みですが、かなり一般的な方法でこの問題を解決しますが、いくつかの明らかな制限があります。私はこのアドバイスをどこにも見たことがなく、誰もがLOHフラグメンテーションについて泣き言を言っているので、コードを共有して、私の設計と仮定が正しいことを確認したいと思いました。

仮説:

  1. 共有の大規模なStringBuilderを作成します(これは、ストリームから読み取った大きな文字列を格納するためのものです)-new StringBuilder(ChunkSize * 5);
  2. 大規模な文字列を作成し(最大許容サイズよりも大きくする必要があります)、空のスペースで初期化する必要があります。-新しい文字列(''、ChunkSize * 10);
  3. 文字列オブジェクトをメモリに固定して、GCが混乱しないようにします。GCHandle.Alloc(pinnedText, GCHandleType.Pinned)。LOHオブジェクトは通常固定されていますが、これによりパフォーマンスが向上するようです。たぶんunsafeコードのせい
  4. ストリームを共有StringBuilderに読み込み、インデクサーを使用して安全でない方法でpinnedTextにコピーします
  5. pinnedTextをRegExに渡します

この実装では、以下のコードはLOH割り当てがない場合と同じように機能します。new string(' ')静的コードを使用する代わりに割り当てに切り替えるStringBuilderか、コードを使用すると、クラッシュする前に300%少ないメモリStringBuilder.ToString()を割り当てることができますoutofmemory exception

また、この実装にはLOHフラグメンテーションがないことを、メモリプロファイラーで確認しました。正規表現が予期しない問題を引き起こさない理由はまだわかりません。また、異なる高価な正規表現パターンでテストしましたが、結果は同じで、断片化はありません。

コード:

http://pastebin.com/ZuuBUXk3

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;

namespace LOH_RegEx
{
    internal class Program
    {
        private static List<string> storage = new List<string>();
        private const int ChunkSize = 100000;
        private static StringBuilder _sb = new StringBuilder(ChunkSize * 5);


        private static void Main(string[] args)
        {
            var pinnedText = new string(' ', ChunkSize * 10);
            var sourceCodePin = GCHandle.Alloc(pinnedText, GCHandleType.Pinned);

            var rgx = new Regex("A", RegexOptions.CultureInvariant | RegexOptions.Compiled);

            try
            {

                for (var i = 0; i < 30000; i++)
                {                   
                    //Simulate that we read data from stream to SB
                    UpdateSB(i);
                    CopyInto(pinnedText);                   
                    var rgxMatch = rgx.Match(pinnedText);

                    if (!rgxMatch.Success)
                    {
                        Console.WriteLine("RegEx failed!");
                        Console.ReadLine();
                    }

                    //Extra buffer to fragment LoH
                    storage.Add(new string('z', 50000));
                    if ((i%100) == 0)
                    {
                        Console.Write(i + ",");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Console.WriteLine("OOM Crash!");
                Console.ReadLine();
            }
        }


        private static unsafe void CopyInto(string text)
        {
            fixed (char* pChar = text)
            {
                int i;
                for (i = 0; i < _sb.Length; i++)
                {
                    pChar[i] = _sb[i];
                }

                pChar[i + 1] = '\0';
            }
        }

        private static void UpdateSB(int extraSize)
        {
            _sb.Remove(0,_sb.Length);

            var rnd = new Random();
            for (var i = 0; i < ChunkSize + extraSize; i++)
            {
                _sb.Append((char)rnd.Next(60, 80));
            }
        }
    }
}
于 2011-11-06T11:21:46.480 に答える
0

ある時点でアンロードされたAppDomainで仕事をすることができますか?

于 2011-11-05T19:19:25.717 に答える
0

1つの代替方法は、非配列ベースのデータ構造で正規表現の一致を実行する方法を見つけることです。残念ながら、クイックGoogleは、ストリームベースの正規表現ライブラリに関してはあまり取り上げませんでした。正規表現アルゴリズムは、ストリームでサポートされていない多くのバックトラッキングを実行する必要があると思います。

正規表現の全力が絶対に必要ですか?すべて85kb未満の文字列のリンクリストで機能する、独自のより単純な検索関数を実装できますか?

また、LOHフラグメンテーションは、ラージオブジェクト参照を長期間保持する場合にのみ実際に問題を引き起こします。あなたが絶えずそれらを作成して破壊しているなら、LOHは成長するべきではありません。

FWIW、私は、LOH内のオブジェクトと断片化のレベルを追跡するのに非常に優れたRedGateANTSメモリプロファイラーを見つけました。

于 2011-11-06T14:25:36.347 に答える