Esta es solo una pregunta para satisfacer mi curiosidad. Pero para mí es interesante.¿Por qué una Regexp en caché supera a una compilada?
Escribí este pequeño punto de referencia simple. Llama a 3 variantes de la ejecución Regexp en orden aleatorio miles de veces:
Básicamente, utilizo el mismo patrón pero de diferentes maneras.
Su forma ordinaria sin ningún
RegexOptions
. Comenzando con .NET 2.0 estos no se almacenan en caché. Pero se debe "almacenar en caché" porque se mantiene en un ámbito bastante global y no se restablece.Con
RegexOptions.Compiled
Con una llamada a la estática
Regex.Match(pattern, input)
el que se pone en caché en el .NET 2,0
Aquí está el código:
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());
}
Cada vez que llamo el resultado está en la línea de:
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
Así que ahí lo tienes. No mucho, pero alrededor de 7-8% de diferencia.
No es el único misterio. No puedo explicar por qué la primera forma sería mucho más lenta porque nunca se vuelve a evaluar sino que se mantiene en una variable estática global.
Por cierto, esto está en .Net 3.5 y Mono 2.2 que se comportan exactamente igual. En Windows.
Entonces, ¿alguna idea, por qué la variante compilada incluso se quedaría atrás?
EDIT1:
Después de fijar el código de los resultados ahora se ven así:
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
que prácticamente obsoleto todas las otras preguntas también.
Gracias por las respuestas.
Ah. :-) Gracias.Actualizaré mi publicación con los nuevos resultados en un minuto. – user51710