Quand ne pas utiliser RegexOptions.Comstackd

Je comprends l’avantage d’utiliser RegexOptions.Comstackd – il améliore le temps d’exécution d’app en ayant l’expression régulière sous forme compilée au lieu de l’interpréter au moment de l’exécution. Bien que cela ne soit pas recommandé pour les applications qui sont déjà lentes au démarrage.

Mais si ma demande peut supporter une légère augmentation du temps de démarrage –
Quels sont les autres scénarios dans lesquels je ne devrais PAS utiliser RegexOptions.Comstackd?

Juste comme une note j’appelle cette méthode plusieurs fois –

private static ssortingng GetName(ssortingng objSsortingng) { return Regex.Replace(objSsortingng, "[^a-zA-Z&-]+", ""); } 

Donc, cette méthode est appelée avec des valeurs différentes pour ‘objSsortingng’ (bien que les valeurs pour objSsortingng puissent également se répéter).

Pensez-vous qu’il est bon / pas bon d’utiliser RegexOptions.Compilé ici? Tout lien Web serait vraiment utile.
Je vous remercie!


MODIFIER

J’ai testé mon application web avec les deux

  • RegexOptions.Comstackd et
  • Instancier Regex tant que variable de classe

Mais je n’ai pas trouvé de grande différence dans le temps pris par mon application Web – La seule chose que j’ai remarquée dans les deux scénarios est la première fois où l’application se charge en double du temps par rapport aux chargements successifs. si j’utilise RegexOptions.Compilé ou non.

Des commentaires pour –
Pourquoi mon application Web prend-elle plus de temps à être traitée par Regex pour la première fois et le temps nécessaire est-il réduit de près de moitié ou moins lors des chargements ultérieurs? PS Cette chose est la même si j’utilise RegexOptions.Compilé ou non.

Pour toute question de performance spécifique comme celle-ci, la meilleure façon de déterminer le moyen le plus rapide consiste à tester les deux et à voir.

En règle générale, il est peu probable que la compilation d’une regex ait beaucoup d’avantages, à moins que vous n’utilisiez beaucoup les regex ou des chaînes très volumineuses. (Ou les deux.) Je pense que c’est plus une optimisation d’essayer après avoir déterminé que vous avez un problème de performance et que vous pensez que cela pourrait vous aider, plutôt que d’essayer au hasard.

Pour une discussion générale sur les inconvénients de RegexOptions.Comstackd , voir cet article de Jeff Atwood ; c’est très vieux, mais d’après ce que j’ai compris, aucun des faits majeurs pertinents n’a changé depuis sa rédaction.

Deux choses à considérer sont que RegexOptions.Comstackd prend du temps processeur et de la mémoire.

Dans cet esprit, il n’y a pratiquement qu’une seule instance où vous ne devriez pas utiliser RegexOptions.Comstackd:

  • Votre expression régulière ne s’exécute que quelques fois et l’accélération nette à l’exécution ne justifie pas le coût de la compilation.

Il y a trop de variables pour prédire et tracer une ligne dans le sable, pour ainsi dire. Il faudrait vraiment tester pour déterminer l’approche optimale. Ou, si vous n’avez pas envie de tester, n’utilisez pas Comstackd avant.

Maintenant, si vous choisissez RegexOptions.Comstackd il est important que vous ne RegexOptions.Comstackd pas avec cela.

La meilleure façon de procéder consiste souvent à définir votre object comme une variable statique pouvant être réutilisée à maintes resockets. Par exemple…

 public static Regex NameRegex = new Regex(@"[^a-zA-Z&-]+", RegexOptions.Comstackd); 

Le problème avec cette approche est que si vous déclarez cela globalement, cela peut être un gaspillage si votre application ne l’utilise pas toujours ou ne l’utilise pas au démarrage. Donc, une approche légèrement différente consisterait à utiliser le chargement paresseux comme je le décris dans l’article que j’ai écrit hier.

Donc, dans ce cas, ce serait quelque chose comme ça …

 public static Lazy NameRegex = new Lazy(() => new Regex("[^a-zA-Z&-]+", RegexOptions.Comstackd)); 

Ensuite, vous faites simplement référence à NameRegex.Value chaque fois que vous souhaitez utiliser cette expression régulière et elle est seulement instanciée lors de son premier access.


RegexOptions.Compilé dans le monde réel

Sur certains de mes sites, j’utilise les routes Regex pour ASP.NET MVC. Et ce scénario est une utilisation parfaite pour RegexOptions.Comstackd . Les itinéraires sont définis au démarrage de l’application Web et sont ensuite réutilisés pour toutes les requêtes ultérieures tant que l’application continue de s’exécuter. Ces expressions régulières sont donc instanciées et compilées une fois et réutilisées des millions de fois.

À partir d’un article de blog BCL , la compilation augmente le temps de démarrage d’un ordre de grandeur, mais diminue les temps d’exécution suivants d’environ 30%. En utilisant ces chiffres, la compilation doit être considérée pour un modèle que vous prévoyez d’évaluer plus de 30 fois. (Bien sûr, comme pour toute optimisation des performances, les deux alternatives doivent être mesurées en termes d’acceptabilité.)

Si les performances sont essentielles pour une expression simple appelée à plusieurs resockets, vous pouvez éviter d’utiliser des expressions régulières. J’ai essayé d’exécuter des variantes environ 5 millions de fois chacune:

Remarque: édité à partir de la version précédente pour corriger les expressions régulières.

  static ssortingng GetName1(ssortingng objSsortingng) { return Regex.Replace(objSsortingng, "[^a-zA-Z&-]+", ""); } static ssortingng GetName2(ssortingng objSsortingng) { return Regex.Replace(objSsortingng, "[^a-zA-Z&-]+", "", RegexOptions.Comstackd); } static ssortingng GetName3(ssortingng objSsortingng) { var sb = new SsortingngBuilder(objSsortingng.Length); foreach (char c in objSsortingng) if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '&') sb.Append(c); return sb.ToString(); } static string GetName4(string objString) { char[] c = objString.ToCharArray(); int pos = 0; int writ = 0; while (pos < c.Length) { char curr = c[pos]; if ((curr >= 'A' && curr <= 'Z') || (curr >= 'a' && curr <= 'z') || curr == '-' || curr == '&') { c[writ++] = c[pos]; } pos++; } return new string(c, 0, writ); } unsafe static string GetName5(string objString) { char* buf = stackalloc char[objString.Length]; int writ = 0; fixed (char* sp = objString) { char* pos = sp; while (*pos != '\0') { char curr = *pos; if ((curr >= 'A' && curr <= 'Z') || (curr >= 'a' && curr <= 'z') || curr == '-' || curr == '&') buf[writ++] = curr; pos++; } } return new string(buf, 0, writ); } 

En exécutant de manière indépendante 5 millions de chaînes ASCII aléatoires, 30 caractères chacune, ont donné systématiquement ces chiffres:

  Method 1: 32.3 seconds (interpreted regex) Method 2: 24.4 seconds (comstackd regex) Method 3: 1.82 seconds (SsortingngBuilder concatenation) Method 4: 1.64 seconds (char[] manipulation) Method 5: 1.54 seconds (unsafe char* manipulation) 

En d’autres termes, la compilation a permis d’obtenir un gain de performance de 25% pour un très grand nombre d’évaluations de ce modèle, la première exécution étant environ trois fois plus lente. Les méthodes qui fonctionnaient sur les tableaux de caractères sous-jacents étaient 12 fois plus rapides que les expressions régulières compilées.

Bien que la méthode 4 ou la méthode 5 puissent apporter des avantages en termes de performances par rapport aux expressions régulières, les autres méthodes peuvent offrir d’autres avantages (maintenabilité, lisibilité, flexibilité, etc.). Ce test simple suggère que, dans ce cas, la compilation de l'expression rationnelle présente un avantage de performance modeste par rapport à son interprétation pour un grand nombre d'évaluations.

La compilation améliore généralement les performances uniquement si vous enregistrez l’object Regex que vous créez. Puisque vous ne sauvegardez pas, dans votre exemple, le Regex, vous ne devez pas le comstackr.

Vous voudrez peut-être restructurer le code de cette façon (note: j’ai ré-écrit le regex à ce que je pense que vous voulez. Avoir le carat de début de ligne dans un groupe répétitif n’a pas beaucoup de sens, et je suppose nom préfixe se termine par un tiret):

  private static readonly Regex ComstackdRegex = new Regex("^[a-zA-Z]+-", RegexOptions.Comstackd); private static ssortingng GetNameComstackd(ssortingng objSsortingng) { return ComstackdRegex.Replace(objSsortingng, ""); } 

J’ai aussi écrit un code de test pour ceci:

  public static void TestSpeed() { var testData = "fooooo-bar"; var timer = new Stopwatch(); timer.Start(); for (var i = 0; i < 10000; i++) Assert.AreEqual("bar", GetNameCompiled(testData)); timer.Stop(); Console.WriteLine("Compiled took " + timer.ElapsedMilliseconds + "ms"); timer.Reset(); timer.Start(); for (var i = 0; i < 10000; i++) Assert.AreEqual("bar", GetName(testData)); timer.Stop(); Console.WriteLine("Uncompiled took " + timer.ElapsedMilliseconds + "ms"); timer.Reset(); } private static readonly Regex CompiledRegex = new Regex("^[a-zA-Z]+-", RegexOptions.Compiled); private static string GetNameCompiled(string objString) { return CompiledRegex.Replace(objString, ""); } private static string GetName(string objString) { return Regex.Replace(objString, "^[a-zA-Z]+-", ""); } 

Sur ma machine, je reçois:

Compilé pris 21ms

Non compilé a pris 37ms