RegexOptions.Compiled
indique au moteur d'expression régulière de compiler l'expression d'expression régulière en IL à l'aide de la génération de code léger ( LCG ). Cette compilation se produit lors de la construction de l'objet et le ralentit fortement . À leur tour, les correspondances utilisant l'expression régulière sont plus rapides.
Si vous ne spécifiez pas cet indicateur, votre expression régulière est considérée comme "interprétée".
Prenons cet exemple:
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]);
});
}
}
Il effectue 4 tests sur 3 expressions régulières différentes. Tout d'abord, il teste une seule correspondance unique (compilée vs non compilée). Ensuite, il teste les correspondances répétées qui réutilisent la même expression régulière.
Les résultats sur ma machine (compilés en version, pas de débogueur attaché)
1000 matchs simples (construire Regex, Match et disposer)
Type | Plateforme | Numéro trivial | Vérification simple des e-mails | Vérification des e-mails ext
-------------------------------------------------- ----------------------------
Interprété | x86 | 4 ms | 26 ms | 31 ms
Interprété | x64 | 5 ms | 29 ms | 35 ms
Compilé | x86 | 913 ms | 3775 ms | 4487 ms
Compilé | x64 | 3300 ms | 21985 ms | 22793 ms
1,000,000 correspondances - réutilisation de l'objet Regex
Type | Plateforme | Numéro trivial | Vérification simple des e-mails | Vérification des e-mails ext
-------------------------------------------------- ----------------------------
Interprété | x86 | 422 ms | 461 ms | 2122 ms
Interprété | x64 | 436 ms | 463 ms | 2167 ms
Compilé | x86 | 279 ms | 166 ms | 1268 ms
Compilé | x64 | 281 ms | 176 ms | 1180 ms
Ces résultats montrent que les expressions régulières compilées peuvent être jusqu'à 60% plus rapides dans les cas où vous réutilisez l' Regex
objet. Cependant, dans certains cas, la construction peut être plus lente de plus de 3 ordres de grandeur .
Il montre également que la version x64 de .NET peut être 5 à 6 fois plus lente en ce qui concerne la compilation d'expressions régulières.
La recommandation serait d' utiliser la version compilée dans les cas où soit
- Vous ne vous souciez pas du coût d'initialisation des objets et avez besoin d'une amélioration supplémentaire des performances. (notez que nous parlons ici de fractions de milliseconde)
- Vous vous souciez un peu du coût d'initialisation, mais réutilisez l'objet Regex tellement de fois qu'il le compensera pendant le cycle de vie de votre application.
Spanner en préparation, le cache Regex
Le moteur d'expressions régulières contient un cache LRU qui contient les 15 dernières expressions régulières qui ont été testées à l'aide des méthodes statiques de la Regex
classe.
Par exemple: Regex.Replace
, Regex.Match
etc .. tous utiliser le cache Regex.
La taille du cache peut être augmentée en définissant Regex.CacheSize
. Il accepte les changements de taille à tout moment au cours du cycle de vie de votre application.
Les nouvelles expressions régulières ne sont mises en cache que par les helpers statiques de la classe Regex. Si vous construisez vos objets, le cache est vérifié (pour une réutilisation et un transfert), cependant, l'expression régulière que vous construisez n'est pas ajoutée au cache .
Ce cache est un cache LRU trivial , il est implémenté à l'aide d'une simple double liste chaînée. Si vous l'augmentez à 5000 et utilisez 5000 appels différents sur les helpers statiques, chaque construction d'expression régulière analysera les 5000 entrées pour voir si elle a déjà été mise en cache. Il y a un verrou autour de la vérification, de sorte que la vérification peut diminuer le parallélisme et introduire un blocage de thread.
Le nombre est fixé assez bas pour vous protéger de cas comme celui-ci, bien que dans certains cas, vous n'ayez pas d'autre choix que de l'augmenter.
Ma forte recommandation serait de ne jamais passer l' RegexOptions.Compiled
option à un assistant statique.
Par exemple:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
La raison en est que vous risquez fortement de manquer le cache LRU, ce qui déclenchera une compilation très coûteuse . De plus, vous n'avez aucune idée de ce que font les bibliothèques dont vous dépendez, vous avez donc peu de capacité à contrôler ou à prédire la meilleure taille possible du cache.
Voir aussi: Blog de l'équipe BCL
Remarque : ceci est pertinent pour .NET 2.0 et .NET 4.0. Certains changements attendus dans la version 4.5 pourraient entraîner une révision de ce dernier.