[解決済み] RegexOptions.Compiledはどのように動作するのですか?
質問
正規表現をコンパイルするものとしてマークするとき、舞台裏では何が起こっているのでしょうか?これはキャッシュされた正規表現とどのように違うのですか?
この情報を使って、計算のコストが性能の向上と比較して無視できる場合はどのように判断するのですか?
どのように解決するのか?
RegexOptions.Compiled
は正規表現エンジンに対して、軽量コード生成(
LCG
). このコンパイルは、オブジェクトの構築時に行われ
大きく
が遅くなる。その代わり、正規表現を使ったマッチはより高速になります。
このフラグを指定しない場合、正規表現は "unterplied"とみなされます。
この例を見てみましょう。
public static void TimeAction(string description, int times, Action func)
{
// warmup
func();
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++)
{
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
var simple = "^\\d+$";
var medium = @"^((to|from)\W)?(?<url>http://[\w\.:]+)/questions/(?<questionId>\d+)(/(\w|-)*)?(/(?<answerId>\d+))?";
var complex = @"^(([^<>()[\]\\.,;:\s@""]+"
+ @"(\.[^<>()[\]\\.,;:\s@""]+)*)|("".+""))@"
+ @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
+ @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
+ @"[a-zA-Z]{2,}))$";
string[] numbers = new string[] {"1","two", "8378373", "38737", "3873783z"};
string[] emails = new string[] { "[email protected]", "sss@s", "[email protected]", "[email protected]" };
foreach (var item in new[] {
new {Pattern = simple, Matches = numbers, Name = "Simple number match"},
new {Pattern = medium, Matches = emails, Name = "Simple email match"},
new {Pattern = complex, Matches = emails, Name = "Complex email match"}
})
{
int i = 0;
Regex regex;
TimeAction(item.Name + " interpreted uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
i = 0;
TimeAction(item.Name + " compiled uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern, RegexOptions.Compiled);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern);
i = 0;
TimeAction(item.Name + " prepared interpreted match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern, RegexOptions.Compiled);
i = 0;
TimeAction(item.Name + " prepared compiled match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
}
}
3種類の正規表現で4つのテストを実行します。最初に シングル コンパイル済みと非コンパイル済みとで、1度だけマッチします。次に、同じ正規表現を再利用する繰り返しマッチをテストします。
私のマシンでの結果 (リリースでコンパイル、デバッガは未装着)
1000件のシングルマッチ (正規表現、マッチ、破棄の構築)
タイプ|プラットフォーム|トリビアルナンバー|シンプルなメールチェック|エクストメールチェック ------------------------------------------------------------------------------ インタプリタ|x86|4ms|26ms|31ms インタープリティッド|x64|5ms|29ms|35ms コンパイル|x86|913ms|3775ms|4487ms コンパイル時|x64|3300ms|21985ms|22793ms
1,000,000マッチ - Regexオブジェクトの再利用
種類|プラットフォーム|些細な数|シンプルなメールチェック|エクストメールチェック ------------------------------------------------------------------------------ インタープリタ|x86|422ms|461ms|2122ms インタープリティッド|x64|436ms|463ms|2167ms コンパイル|x86|279ms|166ms|1268ms コンパイル時|x64|281ms|176ms|1180ms
これらの結果から、コンパイルされた正規表現では、最大で
60%
を再利用する場合、より高速になります。
Regex
オブジェクトを作成します。
しかし
を超える場合があります。
3桁
の方が遅くなります。
ということも示しています。 x64版 の.NETは 5~6倍遅くなる 正規表現のコンパイルに関して
推奨されるのは コンパイルされたバージョンを使用する のどちらかである場合
- オブジェクトの初期化コストを気にせず、余分なパフォーマンスブーストを必要とする。(ここでは1ミリ秒の単位で話していることに注意)
- 初期化コストは多少気になるが、REGEXオブジェクトを何度も再利用するため、アプリケーションのライフサイクルの中でそれを補うことができる。
Regexキャッシュにスパナを刺す
正規表現エンジンには、LRUキャッシュが含まれており、このキャッシュには
Regex
クラスがあります。
例えば
Regex.Replace
,
Regex.Match
などは、すべてRegexキャッシュを使用します。
キャッシュのサイズは
Regex.CacheSize
. アプリケーションのライフサイクルの中で、いつでもサイズの変更を受け入れることができます。
新しい正規表現はキャッシュされるだけ 静的ヘルパーによって を使用します。オブジェクトを作成すると、キャッシュがチェックされます (再利用とバンプが行われます)。 キャッシュに追加されない .
このキャッシュは トリビアル LRUキャッシュは、単純なダブルリンクリストを使って実装されています。たまたまそれを5000に増やし、静的ヘルパーで5000の異なる呼び出しを使用する場合、すべての正規表現の構築は以前にキャッシュされたかどうかを確認するために5000のエントリをクロールします。があります。 ロック を使用するため、チェックによって並列性が低下し、スレッドブロッキングが発生する可能性があります。
このようなケースから身を守るために、かなり低い数値に設定されていますが、場合によっては増やさざるを得ないこともあるでしょう。
私の
強い推奨
となります。
決して
を渡すと
RegexOptions.Compiled
オプションを静的ヘルパーに追加します。
例えば
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
というのも、LRUキャッシュのミスが原因で 超高価な をコンパイルします。さらに、依存するライブラリが何をしているのか分からないので 可能な限り キャッシュのサイズ
こちらもご覧ください。 BCLチームブログ
備考 : これは.NET 2.0と.NET 4.0に関連しています。4.5で予想されるいくつかの変更により、これが修正される可能性があります。
関連
-
[解決済み] アセンブリから型を読み込めなかったエラー
-
[解決済み] 正規表現で変数を使うには?
-
[解決済み] JavaScriptでメールアドレスを検証するのに最適な方法は何ですか?
-
[解決済み] enumを列挙するには
-
[解決済み] C#で文字列のエンコーディングを手動で指定せずに、一貫性のあるバイト表現を得るには?
-
[解決済み] DateTime型の誕生日から年齢を計算するにはどうしたらいいですか?
-
[解決済み] Microsoft Officeをインストールせずに、C#でExcel(.XLSおよび.XLSX)ファイルを作成するにはどうすればよいですか?
-
[解決済み] JavaScriptの正規表現でマッチしたグループにアクセスするにはどうすればよいですか?
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] jQueryセレクタの正規表現
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】コンパイラーエラーメッセージ。コンパイラはエラーコード -532462766 で失敗しました。
-
[解決済み] app.configのマッピングがないアセンブリのapp.configの再マッピングを考慮する。
-
[解決済み] データテーブルがすでに別のデータセットに属している
-
[解決済み] ネットワークの位置からアセンブリをロードすることはできません
-
[解決済み] DockPanelを空きスペースいっぱいに表示させる方法
-
[解決済み] 権限 '*' を持つ SSL/TLS の安全なチャネルを確立できませんでした。
-
[解決済み] .net の OOM 問題を解決する必要があります。
-
[解決済み] .NET Coreと.NET Standard Class Libraryのプロジェクトタイプの違いは何ですか?
-
[解決済み] Entity FrameworkとLINQ to SQLの比較
-
[解決済み] WCF - メッセージサイズのクォータを増加させる方法