Warum übertrifft ein zwischengespeicherter Regexp einen kompilierten?

8

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.

  1. 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.

  2. Mit RegexOptions.Compiled

  3. 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.

    
user51710 09.01.2009, 14:18
quelle

4 Antworten

4

In der Regex.Match-Version suchen Sie nach der Eingabe im Muster. Versuchen Sie, die Parameter zu umgehen.

%Vor%     
Martin Brown 09.01.2009, 14:38
quelle
3

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.

    
P Daddy 09.01.2009 14:37
quelle
0

Wenn Sie immer die gleiche Zeichenfolge mit dem gleichen Muster verwenden, kann dies erklären, warum eine zwischengespeicherte Version etwas schneller ist als eine kompilierte Version.

    
Vincent 09.01.2009 14:37
quelle
0

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:

     
  1. Wenn Sie ein Komponentenentwickler sind, der will   Erstellen einer Bibliothek wiederverwendbarer regulärer Ausdrücke.
  2.   
  3. 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.
  4.   
    
Teoman shipahi 08.03.2016 22:12
quelle

Tags und Links