Dies ist nur eine Frage, um meine Neugier zu befriedigen. Aber für mich ist es interessant.Warum übertrifft ein zwischengespeicherter Regexp einen kompilierten?
Ich schrieb diesen kleinen einfachen Benchmark. Es ruft 3 Varianten der Regexp-Ausführung in zufälliger Reihenfolge ein paar tausend Mal auf:
Grundsätzlich verwende ich das gleiche Muster, aber auf verschiedene Arten.
Ihre normale Art und Weise 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 2,0
Hier in .NET zwischengespeichert Bekommt 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());
}
Jedesmal, wenn ich es so nennen Das Ergebnis ist in etwa wie folgt:
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
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 Variablen gehalten wird.
Übrigens ist dies auf .Net 3.5 und Mono 2.2, die sich genau gleich verhalten. Unter Windows.
Also, irgendwelche Ideen, warum die kompilierte Variante sogar zurückfallen würde?
EDIT1:
Nach der Fixierung des Codes 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 so ziemlich alle anderen Fragen auch obsoletes.
Danke für die Antworten.
Ah. :-) Danke.Ich werde meine Post mit den neuen Ergebnissen in einer Minute aktualisieren. – user51710