RegexOptions.Compiled
indica al motor de expresión regular que compile la expresión de expresión regular en IL utilizando la generación de código ligero ( LCG ). Esta compilación ocurre durante la construcción del objeto y lo ralentiza mucho . A su vez, las coincidencias que utilizan la expresión regular son más rápidas.
Si no especifica este indicador, su expresión regular se considera "interpretada".
Toma este ejemplo:
public static void TimeAction(string description, int times, Action func)
{
// warmup
func();
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++)
{
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
var simple = "^\\d+$";
var medium = @"^((to|from)\W)?(?<url>http://[\w\.:]+)/questions/(?<questionId>\d+)(/(\w|-)*)?(/(?<answerId>\d+))?";
var complex = @"^(([^<>()[\]\\.,;:\s@""]+"
+ @"(\.[^<>()[\]\\.,;:\s@""]+)*)|("".+""))@"
+ @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
+ @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
+ @"[a-zA-Z]{2,}))$";
string[] numbers = new string[] {"1","two", "8378373", "38737", "3873783z"};
string[] emails = new string[] { "sam@sam.com", "sss@s", "sjg@ddd.com.au.au", "onelongemail@oneverylongemail.com" };
foreach (var item in new[] {
new {Pattern = simple, Matches = numbers, Name = "Simple number match"},
new {Pattern = medium, Matches = emails, Name = "Simple email match"},
new {Pattern = complex, Matches = emails, Name = "Complex email match"}
})
{
int i = 0;
Regex regex;
TimeAction(item.Name + " interpreted uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
i = 0;
TimeAction(item.Name + " compiled uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern, RegexOptions.Compiled);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern);
i = 0;
TimeAction(item.Name + " prepared interpreted match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern, RegexOptions.Compiled);
i = 0;
TimeAction(item.Name + " prepared compiled match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
}
}
Realiza 4 pruebas en 3 expresiones regulares diferentes. Primero, prueba una sola vez una partida (compilada vs no compilada). En segundo lugar, prueba las coincidencias repetidas que reutilizan la misma expresión regular.
Los resultados en mi máquina (compilados en la versión, sin depurador adjunto)
1000 partidos individuales (construir Regex, Match y disponer)
Tipo | Plataforma | Número trivial | Verificación simple de correo electrónico | Ext Email Check
-------------------------------------------------- ----------------------------
Interpretado | x86 | 4 ms | 26 ms | 31 ms
Interpretado | x64 | 5 ms | 29 ms | 35 ms
Compilado | x86 | 913 ms | 3775 ms | 4487 ms
Compilado | x64 | 3300 ms | 21985 ms | 22793 ms
1,000,000 coincidencias - reutilizando el objeto Regex
Tipo | Plataforma | Número trivial | Verificación simple de correo electrónico | Ext Email Check
-------------------------------------------------- ----------------------------
Interpretado | x86 | 422 ms | 461 ms | 2122 ms
Interpretado | x64 | 436 ms | 463 ms | 2167 ms
Compilado | x86 | 279 ms | 166 ms | 1268 ms
Compilado | x64 | 281 ms | 176 ms | 1180 ms
Estos resultados muestran que las expresiones regulares compiladas pueden ser hasta un 60% más rápidas para los casos en los que reutiliza el Regex
objeto. Sin embargo, en algunos casos puede ser más de 3 órdenes de magnitud más lento de construir.
También muestra que la versión x64 de .NET puede ser de 5 a 6 veces más lenta cuando se trata de compilar expresiones regulares.
La recomendación sería utilizar la versión compilada en los casos en que
- No le importa el costo de inicialización de objetos y necesita un aumento de rendimiento adicional. (Tenga en cuenta que estamos hablando de fracciones de milisegundos aquí)
- Le importa un poco el costo de inicialización, pero está reutilizando el objeto Regex tantas veces que lo compensará durante el ciclo de vida de la aplicación.
Llave en proceso, el caché Regex
El motor de expresiones regulares contiene un caché LRU que contiene las últimas 15 expresiones regulares que se probaron utilizando los métodos estáticos en la Regex
clase.
Por ejemplo: Regex.Replace
, Regex.Match
etc .. todo el uso de la caché de expresiones regulares.
El tamaño de la memoria caché se puede aumentar mediante la configuración Regex.CacheSize
. Acepta cambios de tamaño en cualquier momento durante el ciclo de vida de su aplicación.
Las nuevas expresiones regulares solo son almacenadas en caché por los ayudantes estáticos en la clase Regex. Sin embargo, si construye sus objetos, la memoria caché se verifica (para su reutilización y eliminación), la expresión regular que construye no se agrega a la memoria caché .
Este caché es un caché LRU trivial , se implementa utilizando una lista simple de doble enlace. Si lo aumenta a 5000 y usa 5000 llamadas diferentes en los ayudantes estáticos, cada construcción de expresión regular rastreará las 5000 entradas para ver si se ha almacenado previamente en caché. Hay un bloqueo alrededor del cheque, por lo que el cheque puede disminuir el paralelismo e introducir el bloqueo de hilos.
El número se establece bastante bajo para protegerse de casos como este, aunque en algunos casos puede que no tenga más remedio que aumentarlo.
Mi recomendación fuerte sería nunca pasarle la RegexOptions.Compiled
opción a un ayudante estático.
Por ejemplo:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
La razón es que está arriesgando mucho una falla en la caché de LRU que desencadenará una compilación súper costosa . Además, no tiene idea de lo que están haciendo las bibliotecas de las que depende, por lo que tiene poca capacidad para controlar o predecir el mejor tamaño posible de la memoria caché.
Ver también: blog del equipo BCL
Nota : esto es relevante para .NET 2.0 y .NET 4.0. Hay algunos cambios esperados en 4.5 que pueden hacer que esto se revise.