Frage

Dies ist nur eine Frage, um meine Neugierde zu befriedigen.Aber für mich ist es interessant.

Ich schrieb dieses kleine, einfache benchmark.Es fordert 3 Variante (N) der Regexp-Ausführung in zufälliger Reihenfolge ein paar tausend mal:

Im Grunde habe ich das gleiche Muster verwenden, aber auf unterschiedliche Weise.

  1. Ihre gewöhnliche Art und Weise ohne RegexOptions.Beginnend mit .NET 2.0 diese nicht zwischengespeichert werden.Sollte aber "im Cache", denn es ist gehalten in eine ziemlich Globale Reichweite und nicht zurückgesetzt.

  2. Mit RegexOptions.Compiled

  3. Durch einen Aufruf der statischen Regex.Match(pattern, input) die zwischengespeichert .NET 2.0

Hier ist der code:

static List<string> Strings = new List<string>();        
static string pattern = ".*_([0-9]+)\\.([^\\.])$";

static Regex Rex = new Regex(pattern);
static Regex RexCompiled = new Regex(pattern, RegexOptions.Compiled);

static Random Rand = new Random(123);

static Stopwatch S1 = new Stopwatch();
static Stopwatch S2 = new Stopwatch();
static Stopwatch S3 = new Stopwatch();

static void Main()
{
  int k = 0;
  int c = 0;
  int c1 = 0;
  int c2 = 0;
  int c3 = 0;

  for (int i = 0; i < 50; i++)
  {
    Strings.Add("file_"  + Rand.Next().ToString() + ".ext");
  }
  int m = 10000;
  for (int j = 0; j < m; j++)
  {
    c = Rand.Next(1, 4);

    if (c == 1)
    {
      c1++;
      k = 0;
      S1.Start();
      foreach (var item in Strings)
      {
        var m1 = Rex.Match(item);
        if (m1.Success) { k++; };
      }
      S1.Stop();
    }
    else if (c == 2)
    {
      c2++;
      k = 0;
      S2.Start();
      foreach (var item in Strings)
      {
        var m2 = RexCompiled.Match(item);
        if (m2.Success) { k++; };
      }
      S2.Stop();
    }
    else if (c == 3)
    {
      c3++;
      k = 0;
      S3.Start();
      foreach (var item in Strings)
      {
        var m3 = Regex.Match(item, pattern);
        if (m3.Success) { k++; };
      }
      S3.Stop();
    }
  }

  Console.WriteLine("c: {0}", c1);
  Console.WriteLine("Total milliseconds: " + (S1.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S1.Elapsed.TotalMilliseconds).ToString());

  Console.WriteLine("c: {0}", c2);
  Console.WriteLine("Total milliseconds: " + (S2.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S2.Elapsed.TotalMilliseconds*((float)c2/(float)c1)).ToString());

  Console.WriteLine("c: {0}", c3);
  Console.WriteLine("Total milliseconds: " + (S3.Elapsed.TotalMilliseconds).ToString());
  Console.WriteLine("Adjusted milliseconds: " + (S3.Elapsed.TotalMilliseconds*((float)c3/(float)c1)).ToString());
}

Jedes mal, wenn ich es nennen, das Ergebnis ist entlang der Linien von:

    Not compiled and not automatically cached:
    Total milliseconds: 6185,2704
    Adjusted milliseconds: 6185,2704

    Compiled and not automatically cached:
    Total milliseconds: 2562,2519
    Adjusted milliseconds: 2551,56949184038

    Not compiled and automatically cached:
    Total milliseconds: 2378,823
    Adjusted milliseconds: 2336,3187176891

So dort haben Sie es.Nicht viel, aber etwa 7-8% Unterschied.

Es ist nicht das einzige Geheimnis.Ich kann nicht erklären, warum die erste Möglichkeit wäre, dass sehr viel langsamer, denn es ist nie neu bewertet, hielt aber in eine Globale statische variable.

By the way, ist dies auf .Net 3.5 und Mono 2.2 das Verhalten genau das gleiche.Auf Windows.

Also, irgendwelche Ideen, warum die kompilierte Variante sogar in Rückstand geraten?

EDIT1:

Nach der Befestigung der code, der die Ergebnisse nun wie folgt Aussehen:

    Not compiled and not automatically cached:
    Total milliseconds: 6456,5711
    Adjusted milliseconds: 6456,5711

    Compiled and not automatically cached:
    Total milliseconds: 2668,9028
    Adjusted milliseconds: 2657,77574842168

    Not compiled and automatically cached:
    Total milliseconds: 6637,5472
    Adjusted milliseconds: 6518,94897724836

Die ziemlich viel obsoletes all die anderen Fragen.

Vielen Dank für die Antworten.

War es hilfreich?

Lösung

In der Regex.Match version, die Sie suchen, die Eingang in die Muster.Tauschen Sie die Parameter um.

var m3 = Regex.Match(pattern, item); // Wrong
var m3 = Regex.Match(item, pattern); // Correct

Andere Tipps

Ich bemerkte ein ähnliches Verhalten.Ich fragte mich auch, warum die kompilierte version wäre langsamer, aber bemerkt, dass über eine bestimmte Anzahl von anrufen, die kompilierte version schneller ist.Also grub ich in Reflektor ein wenig, und ich bemerkte, dass für eine kompilierte Regex, es ist immer noch ein wenig setup, die durchgeführt wird, auf den ersten Ruf (speziell, erstellen Sie eine Instanz des entsprechenden RegexRunner Objekt).

In meinem test habe ich festgestellt, dass wenn ich zog beide an den Konstruktor und eine erste Wegwerf-Aufruf der regex außerhalb der timer starten, das kompilierte regex gewonnen, egal, wie viele Iterationen ich lief.


Übrigens, die Zwischenspeicherung, der Rahmen ist zu tun, wenn statische Regex Methoden ist eine Optimierung ist nur erforderlich, wenn statische Regex Methoden.Das ist, weil jeder Aufruf der statischen Regex - Methode erstellt ein neues Regex Objekt.In der Regex Klasse Konstruktor muss es analysieren Sie die Muster.Das Zwischenspeichern ermöglicht es, nachfolgende Aufrufe von statischen Regex Methoden zur Wiederverwendung der RegexTree analysiert von den ersten Anruf, um dadurch den parsing-Schritt.

Wenn Sie mit Instanz-Methoden auf einer einzigen Regex Objekt, dann ist dies kein Problem.Die Analyse ist immer noch nur einmal durchgeführt (wenn Sie das Objekt erstellen).Darüber hinaus erhalten Sie zu vermeiden, laufen alle anderen code in den Konstruktor sowie die heap-Zuweisung (und nachfolgende garbage collection).

Martin Braun bemerkt dass Sie Umgekehrt die Argumente an Ihre statischen Regex call (guter Fang, Martin).Ich denke, Sie werden feststellen, dass, wenn Sie fix, dass die Instanz (nicht kompiliert) - regex-schlagen wird, die statische Anrufe zu jeder Zeit.Sie sollten auch feststellen, dass, in Anbetracht meiner Ergebnisse vor, die erstellten Instanz schlagen wird, die nicht kompiliert auch.

ABER:Sollten Sie wirklich Lesen Jeff Atwood ' s post auf kompilierte regexes, bevor Sie gehen blind anwenden, die option zu jedem regex, die Sie erstellen.

Wenn Sie immer den gleichen string mit dem gleichen Muster, die erklären, warum eine zwischengespeicherte version ist leicht schneller als eine kompilierte version.

Dies ist aus der Dokumentation;

https://msdn.microsoft.com/en-us/library/gg578045(v=vs. 110).aspx

wenn ein static regular expression Methode wird aufgerufen, und die regelmäßige Ausdruck kann nicht gefunden werden in der cache, die engine für reguläre Ausdrücke konvertiert den regulären Ausdruck, um eine Reihe von Betriebs-codes und speichert Sie Sie im cache.Anschließend konvertiert diese operation codes MSIL so dass der JIT-compiler können Sie ausführen kann. Interpretiert regelmäßige Ausdrücke verringern die Startzeit auf Kosten der langsameren Ausführung Zeit.Weil dieser, Sie sind am besten verwendet, wenn der reguläre Ausdruck ist in einer kleinen Anzahl von Methodenaufrufen, oder, wenn die genaue Anzahl der Aufrufe regulären Ausdruck Methoden ist unbekannt, aber wird erwartet, klein.Als die Anzahl der Methodenaufrufe erhöht, die Leistung gain von der reduzierten startup-Zeit ist überholt durch die langsamere Ausführung Geschwindigkeit.

Im Gegensatz zu interpretierten regulären Ausdrücken kompilierte reguläre Ausdrücke erhöhen die Startzeit execute einzelnen pattern-matching-Methoden schneller.Als Ergebnis, die Leistung nutzen , dass Ergebnisse aus der Zusammenstellung der reguläre Ausdruck erhöht in Verhältnis zu der Anzahl der reguläre Ausdruck Methoden genannt.


Um zusammenzufassen, wir empfehlen, dass Sie verwenden interpretiert reguläre Ausdrücke beim Aufruf regulären Ausdruck Methoden, die mit einem bestimmten regular expression relativ selten.

Sollten Sie benutzen kompilierte reguläre Ausdrücke wenn Sie regelmäßigen Anruf Ausdruck Methoden, die mit einem bestimmten regulären Ausdruck relativ Häufig.


Wie zu erkennen?

Die genaue Schwelle, bei der die langsamere Ausführungsgeschwindigkeit von interpretiert reguläre Ausdrücke überwiegen Gewinne aus Ihren reduzierten Start-Zeit, oder die Schwelle, bei der die langsamere Zeiten starten kompilierte reguläre Ausdrücke überwiegen Gewinne aus Ihren schneller Ausführungsgeschwindigkeit, ist schwer zu bestimmen.Es hängt von einer Vielzahl von Faktoren ab, einschließlich der Komplexität des regulären Ausdrucks und die bestimmte Daten, die er verarbeitet. Um zu bestimmen, ob interpretiert oder kompilierte reguläre Ausdrücke bieten die beste Leistung für Ihr Besondere Anwendung Szenario können Sie die Stopwatch-Klasse vergleichen Sie die Ausführungszeiten.


Kompilierte Reguläre Ausdrücke:

Wir empfehlen Ihnen, kompilieren Sie reguläre Ausdrücke, um eine assembly in die folgenden Situationen:

  1. Wenn Sie ein Komponenten-Entwickler, der will zum erstellen einer Bibliothek wiederverwendbarer reguläre Ausdrücke.
  2. Wenn Sie erwarten, dass Ihre regular expression pattern-matching-Methoden genannt zu werden, ein unbestimmten Anzahl von mal-überall von einmal oder zweimal, um Tausende oder Zehntausende mal.Im Gegensatz zu kompilierten oder interpretiert, reguläre Ausdrücke, reguläre Ausdrücke kompiliert werden separate Baugruppen bieten Leistung, die konsistent ist, unabhängig von der Anzahl der Methodenaufrufe.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top