Dies ist nur eine Frage, um meine Neugier zu befriedigen. Aber für mich ist es interessant.
Ich habe diesen kleinen einfachen Benchmark geschrieben. Es ruft 3 Varianten der Regexp-Ausführung in zufälliger Reihenfolge einige tausend Mal auf:
Grundsätzlich verwende ich das gleiche Muster, aber auf verschiedene Arten.
Ihr ganz normaler Weg ohne RegexOptions
. Ab .NET 2.0 werden diese nicht zwischengespeichert. Aber sollte "zwischengespeichert" werden, weil es in einem ziemlich globalen Bereich gehalten und nicht zurückgesetzt wird.
Mit RegexOptions.Compiled
Mit einem Aufruf der statischen Regex.Match(pattern, input)
, die in .NET 2.0 zwischengespeichert wird
Hier ist der Code:
%Vor%Jedes Mal, wenn ich es nenne, ist das Ergebnis in etwa wie folgt:
%Vor%Also da hast du es. Nicht viel, aber etwa 7-8% Unterschied.
Es ist nicht das einzige Geheimnis. Ich kann nicht erklären, warum der erste Weg so viel langsamer wäre, weil er nie neu bewertet wird, sondern in einer globalen statischen Variable gehalten wird.
Das ist übrigens bei .Net 3.5 und Mono 2.2, die sich genau gleich verhalten. Unter Windows.
Also, irgendwelche Ideen, warum die kompilierte Variante sogar ins Hintertreffen geraten würde?
EDIT1:
Nach dem Reparieren des Codes sehen die Ergebnisse nun so aus:
%Vor%Das übertrifft alle anderen Fragen ebenfalls.
Danke für die Antworten.
In der Regex.Match-Version suchen Sie nach der Eingabe im Muster. Versuchen Sie, die Parameter zu umgehen.
%Vor% Ich bemerkte ähnlich Verhalten. Ich fragte mich auch, warum die kompilierte Version langsamer sein würde, aber bemerkte, dass ab einer bestimmten Anzahl von Aufrufen die kompilierte Version schneller ist. Also habe ich mich ein bisschen in Reflektor vertieft, und mir ist aufgefallen, dass es für einen kompilierten Regex immer noch ein kleines Setup gibt wird beim ersten Aufruf ausgeführt (insbesondere beim Erstellen einer Instanz der entsprechenden RegexRunner
Objekt).
In meinem Test habe ich festgestellt, dass, wenn ich sowohl den Konstruktor als auch einen anfänglichen Wegwerf-Aufruf an die Regex außerhalb des Timer-Starts verschoben habe, die kompilierte Regex gewonnen hat, egal wie viele Iterationen ich ausgeführt habe.
Übrigens ist das Caching, das das Framework ausführt, wenn statische Regex
-Methoden verwendet werden, eine Optimierung, die nur benötigt wird, wenn statische Regex
-Methoden verwendet werden. Dies liegt daran, dass bei jedem Aufruf einer statischen Regex
-Methode ein neues Regex
-Objekt erstellt wird. Im Konstruktor der Klasse Regex
muss das Muster analysiert werden. Durch das Caching können nachfolgende Aufrufe von statischen Regex
-Methoden die vom ersten Aufruf analysierte RegexTree
wiederverwenden, wodurch der Parsing-Schritt vermieden wird.
Wenn Sie Instanzmethoden für ein einzelnes Regex
-Objekt verwenden, ist dies kein Problem. Das Parsen wird nur einmal ausgeführt (wenn Sie das Objekt erstellen). Darüber hinaus müssen Sie vermeiden, den gesamten anderen Code im Konstruktor auszuführen, sowie die Heap-Zuweisung (und die anschließende Speicherbereinigung).
Martin Brown bemerkte , dass Sie Die Argumente für Ihren statischen Regex
-Aufruf wurden umgekehrt (guter Fang, Martin). Ich denke, Sie werden feststellen, dass, wenn Sie das beheben, die Instanz (nicht kompiliert) Regex die statischen Aufrufe jedes Mal schlagen wird. Sie sollten auch feststellen, dass die kompilierte Instanz die oben genannten Ergebnisse auch mit den nicht kompilierten Instanzen vergleicht.
ABER : Sie sollten wirklich Jeff Atwoods Beitrag bei kompilierten Regexen, bevor Sie diese Option blind für jede von Ihnen erstellte Regex anwenden.
Dies ist aus der Dokumentation;
wenn eine statische reguläre Ausdrucksmethode aufgerufen wird und der reguläre Ausdruck kann nicht im Cache gefunden werden, die Engine für reguläre Ausdrücke wandelt den regulären Ausdruck in einen Satz von Operationscodes und Speichern um sie im Cache . Es wandelt dann diese Operationscodes in MSIL um dass der JIT-Compiler sie ausführen kann. Interpretiert regulär Ausdrücke reduzieren die Startzeit auf Kosten einer langsameren Ausführungszeit . Aus diesem Grund sind sie am besten, wenn der reguläre Ausdruck verwendet wird in einer kleinen Anzahl von Methodenaufrufen verwendet , oder wenn die genaue Anzahl von Aufrufe von regulären Ausdrucksmethoden sind unbekannt, werden aber erwartet klein. Wenn die Anzahl der Methodenaufrufe ansteigt, steigt der Leistungsgewinn von reduzierter Startzeit wird durch die langsamere Ausführung übertroffen Geschwindigkeit.
Im Gegensatz zu interpretierten regulären Ausdrücken kompiliert regulär Ausdrücke erhöhen die Startzeit, führen sie jedoch einzeln aus Pattern-Matching-Methoden schneller . Als Ergebnis profitiert die Leistung Das ergibt sich aus dem Kompilieren des regulären Ausdrucks erhöht in Verhältnis zur Anzahl der regulären Ausdrucksmethoden namens.
Zusammenfassend empfehlen wir Ihnen, interpretierte reguläre Ausdrücke zu verwenden, wenn Sie Methoden für reguläre Ausdrücke mit einem bestimmten Aufruf aufrufen regelmäßiger Ausdruck relativ selten.
Sie sollten kompilierte reguläre Ausdrücke verwenden, wenn Sie regulär anrufen Ausdrucksmethoden mit einem bestimmten regulären Ausdruck relativ häufig.
Wie erkennen?
Der genaue Schwellenwert, ab dem die langsameren Ausführungsgeschwindigkeiten von interpretierte reguläre Ausdrücke wiegen die Gewinne ihrer reduzierten auf Startzeit oder der Schwellenwert, bei dem die langsameren Startzeiten von Kompilierte reguläre Ausdrücke überwiegen die Gewinne von ihren schneller Ausführungsgeschwindigkeiten, ist schwer zu bestimmen. Es hängt von einer Vielzahl ab Faktoren, einschließlich der Komplexität des regulären Ausdrucks und der bestimmte Daten, die es verarbeitet. Um festzustellen, ob interpretiert oder kompilierte reguläre Ausdrücke bieten die beste Leistung für Ihre bestimmten Anwendungsszenario können Sie die Stoppuhr-Klasse verwenden vergleiche ihre Ausführungszeiten .
Kompilierte reguläre Ausdrücke:
Wir empfehlen, dass Sie reguläre Ausdrücke zu einer Assembly in die folgenden Situationen:
- Wenn Sie ein Komponentenentwickler sind, der will Erstellen einer Bibliothek wiederverwendbarer regulärer Ausdrücke.
- Wenn Sie es erwarten Die Mustervergleichsmethoden Ihres regulären Ausdrucks werden als Unbestimmte Anzahl von Malen - überall von ein- oder zweimal zu Tausende oder Zehntausende Male. Anders als kompiliert oder interpretierte reguläre Ausdrücke, reguläre Ausdrücke, die kompiliert werden getrennte Baugruppen bieten eine Leistung, die unabhängig ist der Anzahl der Methodenaufrufe.
Tags und Links c# benchmarking regex performance