正規表現におけるコンパイルと再利用

正規表現エンジンが式をどのようにコンパイルするか、および正規表現がどのようにキャッシュされるかを理解して、正規表現を幅広く使用するアプリケーションのパフォーマンスを最適化できます。 この記事では、コンパイルされた正規表現のコンパイル、ソース生成、キャッシュについて説明します。

解釈される正規表現

既定では、正規表現エンジンは、内部命令のシーケンス (共通中間言語 (CIL) とは異なる高度なコード) に正規表現をコンパイルします。 エンジンは、正規表現を実行するときに内部コードを解釈します。

コンパイルされた正規表現

RegexOptions.Compiled オプションを使用して Regex オブジェクトを構築した場合、このオブジェクトは、高度な正規表現の内部命令ではなく明示的な CIL コードに正規表現をコンパイルします。 これにより、.NET の Just-In-Time (JIT) コンパイラは、式をネイティブのマシン コードに変換してパフォーマンスを高めることができます。 Regex オブジェクトの作成にかかるコストは高くなる場合がありますが、それとの照合の実行にかかるコストは大幅に小さくなる可能性があります。

ソース生成される正規表現

正規表現でのソース生成は、.NET 7 以降のバージョンで可能です。 ソース ジェネレーターでは、IL で RegexOptions.Compiled が出力するとの同様のロジックを含むカスタム Regex 派生実装を、C# コードとして出力します。 スループット パフォーマンスに関する RegexOptions.Compiled のすべての利点と、起動時の Regex.CompileToAssembly の利点を、CompileToAssembly の複雑さなしで得られます。 出力されるソースはプロジェクトの一部であるため、簡単に表示およびデバッグすることもできます。

可能であれば、RegexOptions.Compiled オプションを使用して正規表現をコンパイルするのでなく、ソース生成される正規表現を使用してください。 ソース生成される正規表現の詳細については、「.NET 正規表現ソース ジェネレーター」を参照してください。

正規表現のキャッシュ

パフォーマンスを高めるために、正規表現エンジンは、コンパイルされた正規表現のアプリケーション全体のキャッシュを保持します。 キャッシュは、静的メソッド呼び出しでのみ使用される正規表現パターンを格納します (インスタンス メソッドに渡される正規表現パターンはキャッシュされません。)キャッシュを使用すると、式を使用するたびに高度なバイト コードに再解析する必要がなくなります。

キャッシュされる正規表現の最大数は、static (Visual Basic では Shared) Regex.CacheSize プロパティの値によって決定されます。 既定では、正規表現エンジンは最大 15 個のコンパイルされた正規表現をキャッシュします。 コンパイルされた正規表現の数がキャッシュ サイズを超えた場合は、最近の使用頻度が最も低い正規表現が破棄され、新しい正規表現がキャッシュされます。

アプリケーションでは、次の 2 つの方法のいずれかで正規表現を再利用できます。

  • Regex オブジェクトの静的メソッドを使用して、正規表現を定義する。 別の静的メソッド呼び出しで既に定義されている正規表現パターンを使用している場合、正規表現エンジンではキャッシュからのその取得が試みられます。 キャッシュに使用できるものがない場合、エンジンによって正規表現がコンパイルされてキャッシュに追加されます。
  • 既存の Regex オブジェクトの正規表現パターンが必要な間、このオブジェクトを再利用する。

オブジェクトのインスタンス化および正規表現のコンパイルのオーバーヘッドが原因となり、さまざまな Regex オブジェクトを作成してすぐに破棄するプロセスはコストがかかります。 多数の異なる正規表現を使用するアプリケーションの場合は、静的 Regex メソッドの呼び出しを使用することで、および場合によっては正規表現キャッシュのサイズを大きくすることで、パフォーマンスを最適化できます。

関連項目