mars
2010
Une idée pas forcément très intéressante m’est venue récemment, en terme de comparaison de performances.
Entre ces trois notations, fonctionnellement identiques, laquelle est la plus « performante » :
- chaine.ToLower().IndexOf(token) != -1
- chaine.IndexOf(token, StringComparison.CurrentCultureIgnoreCase) != -1
- chaine.ToLower().Contains(token)
Pour garder une approche « pseudo-scientifique », on va partir des données suivantes :
- trois chaines de recherche, une en caractères minuscules, une en majuscule, une mixte (un caractère en majuscule, l’autre en minuscule)
- les chaines contiennent « azertyuiopqsdfghjklmwxcvbntestderechercheazertyuiopqsdfghjklmwxcvbn », et on cherche « testderecherche » dedans
- pour chaque fonction, on va tester les 3 chaines X fois (X étant égal à 100 000)
Et pour corser les choses, on va ajouter à ces tests un petit test ne pouvant pas trouver le résultat (on enlève une lettre du groupe recherché dans la chaine à laquelle on applique la recherche).
Et au final.
Si la chaine recherchée peut être trouvée :
- chaine.ToLower().IndexOf(token) -> 00:00:00.44
- chaine.IndexOf(token, StringComparison.CurrentCultureIgnoreCase) -> 00:00:00.29
- chaine.ToLower().Contains(token)-> 00:00:00.23
Si la chaine recherchée ne peut être trouvée :
- chaine.ToLower().IndexOf(token) -> 00:00:00.70
- chaine.IndexOf(token, StringComparison.CurrentCultureIgnoreCase) -> 00:00:00.57
- chaine.ToLower().Contains(token)-> 00:00:00.27
Moralité, l’idéal est d’utiliser Contains
Bon, comme d’habitude, à prendre avec un grain de sel, la différence de coût est minime.
Mais dans ce cas précis, on obtient non seulement du code plus performant, mais aussi (et surtout) plus lisible.alors, n’hésitez pas
9 Commentaires + Ajouter un commentaire
Articles récents
Archives
- janvier 2014
- septembre 2013
- août 2013
- mai 2013
- avril 2013
- janvier 2013
- août 2012
- juin 2012
- mai 2012
- avril 2012
- mars 2012
- novembre 2011
- septembre 2011
- août 2011
- juillet 2011
- juin 2011
- mai 2011
- avril 2011
- février 2011
- janvier 2011
- novembre 2010
- octobre 2010
- septembre 2010
- août 2010
- juillet 2010
- juin 2010
- mai 2010
- avril 2010
- mars 2010
- février 2010
- janvier 2010
- décembre 2009
- novembre 2009
- octobre 2009
- septembre 2009
- août 2009
- juillet 2009
- juin 2009
- mai 2009
- avril 2009
- mars 2009
- février 2009
- janvier 2009
@adiGuba: stopwatch a en theorie une precision de l’ordre de la nanoseconde (je crois que spring fourni une implementation equivalente aussi appelee stopwatch).
Je dis en theorie parce que ca depend du hardware sous jascent, a verifier en utilisant la propriete IsHighResolution, et que en une nanoseconde, il se passe beaucoup de choses :).
Je ne parlais pas de problème d’arrondis mais de précision
Je ne connais pas le fonctionnement de Stopwatch, mais certains timers ont une fréquence de rafraichissement assez important qui peut fausser les résultats…
a++
@adiGuba : la mesure est faite avec le stopwatch, donc, normalement, pas de lézard sur les arrondis, et c’est de la millisedonde
Le dilemme était que justement, je voulais vérifier que ce soit bien le code le plus lisible le plus performant
@Nip : oui, c’est encore mieux dans ce cas…ceci dit, tant qu’à faire une méthode plus lisible un ContainsIgnoreCase(this string source, string value), ca le fait peut-être encore plus
il est toujours possible de passer par une methode d’extension dans ce dernier cas
2
3
4
5
public static bool Contains(this string source, string value, StringComparison comparison) <br />
{ <br />
return source.IndexOf(value, comparison) >= 0; <br />
} <br />
Bon, et avec ordinal, tout change…
Si la chaine recherchée peut être trouvée :
* chaine.ToLower().IndexOf(token) -> 00:00:00.44
* chaine.IndexOf(token, StringComparison.CurrentCultureIgnoreCase) -> 00:00:00.29
* chaine.ToLower().Contains(token)-> 00:00:00.23
* chaine.ToLower().IndexOf(token, StringComparison.Ordinal)-> 00:00:00.23
* chaine.IndexOf(token, StringComparison.OrdinalIgnoreCase)-> 00:00:00.09
Si la chaine recherchée ne peut être trouvée :
* chaine.ToLower().IndexOf(token) -> 00:00:00.70
* chaine.IndexOf(token, StringComparison.CurrentCultureIgnoreCase) -> 00:00:00.57
* chaine.ToLower().Contains(token)-> 00:00:00.26
* chaine.ToLower().IndexOf(token, StringComparison.Ordinal)-> 00:00:00.26
* chaine.IndexOf(token, StringComparison.OrdinalIgnoreCase)-> 00:00:00.15
Et la, ca calme encore un peu plus
Dommage que le contains n’ait pas une surcharge pour IgnoreCase, mais bon, une petite surcharge, et on y est
> et une bonne regex ?
Bah en fait, je suis parti du cas « bête et méchant », j’avoue que je n’ai même pas pensé à testeer la regex…
> Je suis quand meme surpris de ces resultats; la methode Contains appelle IndexOf
Moi aussi, j’ai été surpris, sur le coup, parce que ca me semblait logique…ceci dit, je pense tenir le coupable, à savoir le StringComparison.Ordinal, qui doit effectivement faire gagner en perf…
Plus qu’à ajouter StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase et les regex
Tant qu’il n’y a pas de problème de performance bien ciblé, il faut toujours utiliser le code le plus simple et le plus lisible
La différence entre IndexOf() et Contains() est un peu étrange puisque c’est sensiblement la même chose.
Le temps est indiqué en quoi précisément ? Attention car certaine méthode ont une précision assez large du style 32 ms ce qui peut aboutir sur des écarts qui n’en sont pas réellement…
@nico-pyright(c) : les regexp sont plus couteuse pour une simple recherche de texte. En contrepartie elles offrent bien plus de possibilité
a++
Je suis quand meme surpris de ces resultats; la methode Contains appelle IndexOf
2
3
4
5
public bool Contains(string value) <br />
{ <br />
return (this.IndexOf(value, StringComparison.Ordinal) >= 0); <br />
} <br />
Reste que ce tu dis est vrai et je prefere utiliser Contains qui est bien plus lisible a mon gout.
et une bonne regex ?