Comment fonctionne RegexOptions.Comstackd?

Que se passe-t-il dans les coulisses lorsque vous marquez une expression régulière comme une expression à comstackr? En quoi cette comparaison / diffère-t-elle d’une expression régulière mise en cache?

En utilisant ces informations, comment déterminez-vous le coût du calcul par rapport à l’augmentation des performances?

RegexOptions.Comstackd indique au moteur d’expression régulière de comstackr 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 pendant la construction de l’object 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(ssortingng 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)?(?http://[\w\.:]+)/questions/(?\d+)(/(\w|-)*)?(/(?\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,}))$"; ssortingng[] numbers = new ssortingng[] {"1","two", "8378373", "38737", "3873783z"}; ssortingng[] emails = new ssortingng[] { "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 + " comstackd uncached single match (x1000)", 1000, () => { regex = new Regex(item.Pattern, RegexOptions.Comstackd); 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.Comstackd); i = 0; TimeAction(item.Name + " prepared comstackd match (x1000000)", 1000000, () => { regex.Match(item.Matches[i++ % item.Matches.Length]); }); } } 

Il effectue 4 tests sur 3 expressions régulières différentes. D’abord, il teste une seule correspondance unique (compilé vs non compilé). Deuxièmement, 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 dans la version, aucun débogueur attaché)

1000 correspondances simples (construire Regex, Match et disposer)

 Type |  Plateforme |  Numéro sortingvial |  Simple Email Check |  Ext Email Check
 -------------------------------------------------- ----------------------------
 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 de correspondances – réutilisation de l’object Regex

 Type |  Plateforme |  Numéro sortingvial |  Simple Email Check |  Ext Email Check
 -------------------------------------------------- ----------------------------
 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’object Regex . Cependant, dans certains cas, il peut être plus difficile de construire 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 des expressions régulières.


La recommandation serait d’ utiliser la version compilée dans les cas où

  1. Vous ne vous souciez pas des coûts d’initialisation des objects et vous avez besoin d’une augmentation des performances supplémentaire. (note nous parlons des fractions d’une milliseconde ici)
  2. Vous vous souciez un peu du coût d’initialisation, mais réutilisez autant de fois l’object Regex qu’il le compensera pendant le cycle de vie de votre application.

Clé dans les travaux, le cache Regex

Le moteur d’expressions régulières contient un cache LRU qui contient les 15 dernières expressions régulières testées à l’aide des méthodes statiques de la classe Regex .

Par exemple: Regex.Replace , Regex.Match etc. utilisent tous le cache Regex.

La taille du cache peut être augmentée en définissant Regex.CacheSize . Il accepte les changements de taille à tout moment pendant le cycle de vie de votre application.

Les nouvelles expressions régulières ne sont mises en cache que par les aides statiques de la classe Regex. Si vous construisez vos objects, le cache est vérifié (pour être réutilisé et remplacé). Cependant, l’expression régulière que vous construisez n’est pas ajoutée au cache .

Ce cache est un cache sortingvial de LRU, il est implémenté à l’aide d’une simple liste de liens doubles. Si vous augmentez le nombre à 5000 et que vous utilisez 5000 appels différents sur les assistants statiques, chaque construction d’expression régulière parsing 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 le blocage des threads.

Le nombre est assez bas pour vous protéger contre de tels cas, bien que dans certains cas, vous n’ayez d’autre choix que de l’augmenter.

Ma recommandation forte serait de ne jamais passer l’option RegexOptions.Comstackd à un assistant statique.

Par exemple:

 \\ WARNING: bad code Regex.IsMatch("10000", @"\\d+", RegexOptions.Comstackd) 

La raison en est que vous risquez fortement un échec sur 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 possibilité de contrôler ou de prévoir 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 4.5 peuvent entraîner une révision de ce paramètre.

Cette entrée dans le blog de l’équipe BCL donne une belle vue d’ensemble: ” Performances de l’expression régulière “.

En bref, il existe trois types de regex (chacun s’exécutant plus rapidement que le précédent):

  1. interprété

    rapide à créer à la volée, lent à exécuter

  2. compilé (celui que vous semblez poser sur)

    plus lent à créer à la volée, rapide à exécuter (bon pour l’exécution en boucle)

  3. pré-compilé

    créer au moment de la compilation de votre application (pas de pénalité de création au moment de l’exécution), rapide à exécuter

Donc, si vous avez l’intention d’exécuter le regex une seule fois, ou dans une section de votre application qui n’est pas critique en termes de performances (c.-à-d. Validation des entrées utilisateur), vous pouvez utiliser l’option 1.

Si vous avez l’intention d’exécuter le regex en boucle (c’est-à-dire l’parsing syntaxique du fichier), vous devez utiliser l’option 2.

Si vous avez de nombreuses expressions rationnelles qui ne changeront jamais pour votre application et qui sont utilisées intensivement, vous pouvez opter pour l’option 3.

Il convient de noter que les performances des expressions régulières depuis .NET 2.0 ont été améliorées avec un cache MRU d’expressions régulières non compilées. Le code de la bibliothèque Regex ne réinterprète plus la même expression régulière non compilée à chaque fois.

Il y a donc potentiellement une plus grande pénalité de performance avec une expression régulière compilée et à la volée. En plus des temps de chargement plus longs, le système utilise également plus de mémoire pour comstackr l’expression régulière vers des opcodes.

Essentiellement, le conseil actuel est soit de ne pas comstackr une expression régulière, soit de les comstackr préalablement à un assembly séparé.

Ref: BCL Team Blog Performance de l’expression régulière [David Gutierrez]

1) Equipe de bibliothèque de classes de base sur les regex compilées

2) Horreur de codage, référence n ° 1 avec quelques bons points sur les compromis