RegexOptions.Compiled
Weist die Engine für reguläre Ausdrücke an, den Ausdruck für reguläre Ausdrücke mithilfe der Lightweight Code Generation ( LCG ) in IL zu kompilieren . Diese Kompilierung erfolgt während der Konstruktion des Objekts und verlangsamt es erheblich . Übereinstimmungen mit dem regulären Ausdruck sind wiederum schneller.
Wenn Sie dieses Flag nicht angeben, wird Ihr regulärer Ausdruck als "interpretiert" betrachtet.
Nehmen Sie dieses Beispiel:
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[] { "sam@sam.com", "sss@s", "sjg@ddd.com.au.au", "onelongemail@oneverylongemail.com" };
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]);
});
}
}
Es führt 4 Tests mit 3 verschiedenen regulären Ausdrücken durch. Zuerst wird ein einzelnes einmaliges Spiel getestet (kompiliert gegen nicht kompiliert). Zweitens werden Wiederholungsübereinstimmungen getestet, bei denen derselbe reguläre Ausdruck wiederverwendet wird.
Die Ergebnisse auf meinem Computer (in der Version kompiliert, kein Debugger angehängt)
1000 einzelne Übereinstimmungen (Regex konstruieren, abgleichen und entsorgen)
Geben Sie | ein Plattform | Trivial Number | Einfache E-Mail-Prüfung | Ext Email Check
-------------------------------------------------- ----------------------------
Interpretiert | x86 | 4 ms | 26 ms | 31 ms
Interpretiert | x64 | 5 ms | 29 ms | 35 ms
Zusammengestellt | x86 | 913 ms | 3775 ms | 4487 ms
Zusammengestellt | x64 | 3300 ms | 21985 ms | 22793 ms
1.000.000 Übereinstimmungen - Wiederverwendung des Regex-Objekts
Geben Sie | ein Plattform | Trivial Number | Einfache E-Mail-Prüfung | Ext Email Check
-------------------------------------------------- ----------------------------
Interpretiert | x86 | 422 ms | 461 ms | 2122 ms
Interpretiert | x64 | 436 ms | 463 ms | 2167 ms
Zusammengestellt | x86 | 279 ms | 166 ms | 1268 ms
Zusammengestellt | x64 | 281 ms | 176 ms | 1180 ms
Diese Ergebnisse zeigen, dass kompilierte reguläre Ausdrücke in Fällen, in denen Sie das Objekt wiederverwenden , bis zu 60% schneller sein können Regex
. In einigen Fällen kann die Konstruktion jedoch um mehr als 3 Größenordnungen langsamer sein.
Es zeigt auch, dass die x64-Version von .NET beim Kompilieren regulärer Ausdrücke fünf- bis sechsmal langsamer sein kann.
Die Empfehlung wäre, die kompilierte Version in Fällen zu verwenden, in denen dies auch der Fall ist
- Sie interessieren sich nicht für die Kosten der Objektinitialisierung und benötigen die zusätzliche Leistungssteigerung. (Beachten Sie, dass es sich hier um Bruchteile einer Millisekunde handelt.)
- Sie interessieren sich ein wenig für die Initialisierungskosten, verwenden das Regex-Objekt jedoch so oft wieder, dass es dies während Ihres Anwendungslebenszyklus kompensiert.
Spanner in Arbeit, der Regex-Cache
Die Engine für reguläre Ausdrücke enthält einen LRU-Cache, der die letzten 15 regulären Ausdrücke enthält, die mit den statischen Methoden für die Regex
Klasse getestet wurden .
Zum Beispiel: Regex.Replace
, Regex.Match
etc .. verwenden alle die Regex - Cache.
Die Größe des Caches kann durch Einstellen erhöht werden Regex.CacheSize
. Es akzeptiert jederzeit Größenänderungen während des Lebenszyklus Ihrer Anwendung.
Neue reguläre Ausdrücke werden nur von den statischen Helfern der Regex-Klasse zwischengespeichert. Wenn Sie Ihre Objekte erstellen, wird der Cache überprüft (auf Wiederverwendung und Unebenheiten). Der reguläre Ausdruck, den Sie erstellen, wird jedoch nicht an den Cache angehängt .
Dieser Cache ist ein trivialer LRU-Cache, der mithilfe einer einfachen doppelt verknüpften Liste implementiert wird. Wenn Sie es auf 5000 erhöhen und 5000 verschiedene Aufrufe für die statischen Helfer verwenden, werden bei jeder Konstruktion mit regulären Ausdrücken die 5000 Einträge gecrawlt, um festzustellen, ob sie zuvor zwischengespeichert wurden. Um die Prüfung herum befindet sich eine Sperre , sodass die Prüfung die Parallelität verringern und eine Thread-Blockierung einführen kann.
Die Anzahl ist ziemlich niedrig eingestellt, um sich vor solchen Fällen zu schützen. In einigen Fällen haben Sie jedoch möglicherweise keine andere Wahl, als sie zu erhöhen.
Meine starke Empfehlung wäre, die Option niemalsRegexOptions.Compiled
an einen statischen Helfer weiterzugeben .
Beispielsweise:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
Der Grund dafür ist, dass Sie einen Fehlschlag im LRU-Cache riskieren, der eine super teure Kompilierung auslöst . Darüber hinaus haben Sie keine Ahnung, was die Bibliotheken tun, von denen Sie abhängig sind. Daher können Sie die bestmögliche Größe des Caches kaum steuern oder vorhersagen .
Siehe auch: BCL-Teamblog
Hinweis : Dies ist relevant für .NET 2.0 und .NET 4.0. Es gibt einige erwartete Änderungen in 4.5, die dazu führen können, dass dies überarbeitet wird.