Was passiert hinter den Kulissen, wenn Sie einen regulären Ausdruck als zu kompilierenden markieren? Wie unterscheidet sich dieser Vergleich von einem zwischengespeicherten regulären Ausdruck?
Wie können Sie anhand dieser Informationen feststellen, wann die Berechnungskosten im Vergleich zur Leistungssteigerung vernachlässigbar sind?
Antworten:
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:
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)
1.000.000 Übereinstimmungen - Wiederverwendung des Regex-Objekts
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
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 niemals
RegexOptions.Compiled
an einen statischen Helfer weiterzugeben .Beispielsweise:
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.
quelle
Compiled
Website-Code, in dem ich tatsächlich ein statisches (anwendungsweites)Regex
Objekt speichere . Das muss alsoRegex
nur einmal erstellt werden, wenn IIS die Anwendung startet, und wird dann tausende Male wiederverwendet. Dies funktioniert gut, solange die Anwendung nicht häufig neu gestartet wird.Dieser Eintrag im BCL Team Blog gibt einen schönen Überblick: " Regular Expression Performance ".
Kurz gesagt, es gibt drei Arten von Regex (jede wird schneller ausgeführt als die vorherige):
interpretiert
schnell im laufenden Betrieb zu erstellen, langsam auszuführen
kompiliert (die, nach der Sie zu fragen scheinen)
langsamer im laufenden Betrieb zu erstellen, schnell auszuführen (gut für die Ausführung in Schleifen)
vorkompiliert
Erstellen Sie zur Kompilierungszeit Ihrer App (keine Laufzeiterstellungsstrafe) und lassen Sie sie schnell ausführen
Wenn Sie also beabsichtigen, den regulären Ausdruck nur einmal oder in einem nicht leistungskritischen Abschnitt Ihrer App auszuführen (dh Überprüfung der Benutzereingaben), ist Option 1 in Ordnung.
Wenn Sie den regulären Ausdruck in einer Schleife ausführen möchten (dh zeilenweises Parsen der Datei), sollten Sie Option 2 wählen.
Wenn Sie viele reguläre Ausdrücke haben, die sich für Ihre App nie ändern und intensiv genutzt werden, können Sie Option 3 wählen.
quelle
CompileModule
. Verdammt, ich muss mir die neue Plattform genauer ansehen.Es ist zu beachten, dass die Leistung regulärer Ausdrücke seit .NET 2.0 durch einen MRU-Cache nicht kompilierter regulärer Ausdrücke verbessert wurde. Der Regex-Bibliothekscode interpretiert nicht mehr jedes Mal denselben nicht kompilierten regulären Ausdruck neu.
So gibt es möglicherweise eine größere Leistung Strafe mit einem kompilierten und on the fly regulären Ausdruck. Zusätzlich zu langsameren Ladezeiten verwendet das System auch mehr Speicher, um den regulären Ausdruck für Opcodes zu kompilieren.
Im Wesentlichen lautet der aktuelle Rat, entweder keinen regulären Ausdruck zu kompilieren oder diese vorab in einer separaten Assembly zu kompilieren.
Ref: BCL Team Blog Regular Expression Performance [David Gutierrez]
quelle
1) Basisklassenbibliotheksteam auf kompiliertem regulären Ausdruck
2) Coding Horror, unter Bezugnahme auf # 1 mit einigen guten Punkten zu den Kompromissen
quelle
Ich hoffe, der folgende Code hilft Ihnen, das Konzept der re.compile-Funktionen zu verstehen
quelle