Optimisation : Les POSIX (ereg) et les PCRE (preg)
Derrière ces deux noms barbares, les POSIX et les PCRE, se cache deux librairies d’expressions rationnelles. Pour être plus parlant, lorsque vous utilisez les fonctions « ereg » vous utilisez la librairie POSIX et lorsque vous utilisez les fonctions « preg » vous utilisez la librairie PCRE. A noter tout de même que les deux librairies (et donc les fonctions associées) permettent de faire exactement les mêmes choses.
Comme vous pouvez le remarquer sur la documentation officielle de PHP vous trouverez ce genre de mention lorsque vous consultez la documentation des fonctions ereg :
Note: preg_match(), qui utilise la syntaxe des expressions rationnelles compatibles PERL, est une alternative plus rapide de ereg()
Notez aussi que vous avez désormais cette mention qui apparait :
Cette fonction est OBSOLETE depuis PHP 5.3.0 et a été SUPPRIMEE depuis PHP 6.0.0. Nous vous encourageons vivement à ne plus l’utiliser.
Alors pourquoi l’utilisation des POSIX est déconseillée, pourquoi depuis PHP 5.3 elles sont devenues obsolètes et enfin pourquoi elles vont être supprimée dans PHP 6 ? La réponse dans la suite de cette article…
Pour faire ce test de performance, on va utiliser le même protocole de test que dans l’article sur la concaténation. Nous allons utiliser sur ce petit test la validation d’une adresse email. Nous étudierons deux cas, le cas ou l’adresse email est bonne et le cas ou elle ne l’est pas. Nous baserons nos tests sur 100 000 itérations pour révéler clairement les différences de performance. Les regex dans les tests seront tirés du très bon site Expreg.
- Cas avec une adresse email correcte
Voici le code que nous allons utiliser pour ce premier test, avec la fonction ereg (POSIX):
<?php include('Benchmark.class.php'); $var = 'info@e-ur-ope.a.n-cards.com'; $perf = new Benchmark(); for($i=0;$i<100000;$i++){ ereg("^[[:alnum:]]([-_.]?[[:alnum:]])*@[[:alnum:]]([-.]?[[:alnum:]])*\.([a-z]{2,4})$",$var); } $perf->stop(); echo $perf->getStats().' secondes'; ?>
Et voici celui pour preg_match (PCRE):
<?php include('Benchmark.class.php'); $var = 'info@e-ur-ope.a.n-cards.com'; $perf = new Benchmark(); for($i=0;$i<100000;$i++){ preg_match('`^[[:alnum:]]([-_.]?[[:alnum:]])*@[[:alnum:]]([-.]?[[:alnum:]])*\.([a-z]{2,4})$`',$var); } $perf->stop(); echo $perf->getStats().' secondes'; ?>
Voici les résultats dans un graphique :
On se rend compte que la fonction ereg est ainsi 38% plus lente que la fonction preg_match sur ce simple test, un écart de performance qui est tout de même assez conséquent et qui commence à confirmer le choix de la team PHP de vouloir supprimer les fonctions POSIX…
- Cas avec une adresse email incorrecte
Nous allons maintenant faire les mêmes tests avec une chaîne invalide qui est :
$var = 'info@e-ur-ope.a.n-cards .com';
Voici les résultats :
On constate ainsi plusieurs choses avec ce graphique, la première c’est que pour l’une ou l’autre des fonctions le temps de réponse est plus court que lors du premier test, ce qui est normal par rapport au fonctionnement d’une expression régulière. Deuxième observation, on remarque que l’écart de performance est pour ainsi dire identique (37%) que lors du premier test, mais est ce que cet écart est toujours le même avec une chaîne plus longue ? C’est ce que nous allons voir :
$var = 'une_tr_longu_adresse_email_qui_est_interminable_info@e-ur-ope.a.n-cards .com';
Et voici le résultat :
Vous le voyez vous même… L’écart de performance commence sérieusement à grimper, la fonction ereg est ainsi 73% plus lente que la fonction preg_match, échec et mat !
En conclusion de ce bref test (est-il utile d’en faire plus ?) on remarque bien l’écart important de performance entre les POSIX et les PCRE qui devient d’autant plus important lorsque l’on travaille sur de longues chaînes de caractères mais aussi en fonction de la complexité du masque. L’utilisation des POSIX est donc à bannir de vos scripts et personne ne regrettera le choix de PHP de… L’abandonner !
Pour aller plus loin les expressions régulières sont des fonctions lourdes et je ne saurais trop vous conseiller de ne les utiliser que lorsque vous en avez réellement besoin (cf le bonus ci-dessous).
Bonus: Pour aller plus loin…
Imaginons que vous voulez détecter la présence d’un mot dans une chaîne de caractéres, je remarque souvent que beaucoup utilisent une expression régulière plutôt que strpos. J’ai toujours été partisan d’utiliser strpos dans ce cas de figure car je pense qu’une fonction native dédiée à ce que l’on veut est plus rapide… Voici donc un petit test avec preg_match :
<?php include('Benchmark.class.php'); $var = 'Check this string for finding a GoogleBot !'; $perf = new Benchmark(); for($i=0;$i<300000;$i++){ if(preg_match('`^.*GoogleBot.*$`',$var)){ // Action } } $perf->stop(); echo $perf->getStats().' secondes'; ?>
Et le même avec strpos :
<?php include('Benchmark.class.php'); $var = 'Check this string for finding a GoogleBot !'; $perf = new Benchmark(); for($i=0;$i<300000;$i++){ if(strpos($var,'GoogleBot') !== false){ // Action } } $perf->stop(); echo $perf->getStats().' secondes'; ?>
Ce test consiste simplement à trouver le mot « GoogleBot » dans une chaîne de caractères. Et voici le résultat :
On remarque ici que l’expression régulière est 11% plus lente que la fonction strpos, tout en considérant que plus la chaîne à analyser est longue, plus l’écart sera important. Il est donc préférable dans ce cas d’utiliser strpos plutôt qu’une expression régulière.
Dans le même esprit PHP a introduit récemment les fonctions filter, il est ainsi possible de tester la validité d’une adresse email avec celles-ci. Mais qu’en est il question performance ? Nous allons pour ce faire reprendre les résultats lors de notre premier test avec preg_match et nous allons les confronter avec ce test :
<?php include('Benchmark.class.php'); $var = 'info@e-ur-ope.a.n-cards.com'; $perf = new Benchmark(); for($i=0;$i<100000;$i++){ filter_var($var, FILTER_VALIDATE_EMAIL); } $perf->stop(); echo $perf->getStats().' secondes'; ?>
Voici les résultats :
On se rend malheureusement compte que la fonction filter_var est ainsi environ 10% plus lente que la fonction preg_match… Il y a encore du travail de ce coté, et pour ne pas arranger les choses le filtre FILTER_VALIDATE_EMAIL comporte de nombreux bugs… je vous déconseille donc de l’utiliser à l’heure actuelle et de préférer les expressions régulières.
include(‘Benchmark.class.php’);
$var = ‘Check this string for finding a GoogleBot !’;
$perf = new Benchmark();
for($i=0;$i<100000;$i++){
if(preg_match(‘`^.*GoogleBot.*$`’,$var)){
// Action
}
}
$perf->stop();
echo $perf->getStats().’ secondes’;
?>
| Imprimer l'article | Cette entrée a été posté par Steuf le 2 juillet 2010 à 15 h 00 min, et placée dans Optimisation, PHP5/POO. Vous pouvez suivre les réponses à cette entrée via RSS 2.0. Vous pouvez laisser une réponse, ou bien un trackback depuis votre site. |





about 1 year ago
Good test, en plus je boss sur ça en ce moment :p
Merci
about 1 year ago
tu peux aussi ajouter un test de perf, entre différentes expressions?
about 1 year ago
Ce genre de test est inutile car la réponse est évidente et signaler dans l’article il me semble, plus ton masque offre de possibilités et plus ta chaîne à analyser sera longue, plus le temps de traitement sera long. C’est aussi simple que ça, cela vient du fonctionne des regex qui va fait tenter de valider ton masque avec toutes les combinaisons possibles.
about 1 year ago
En regardant bien, suis pas très fan de la regex d’expreg qui matchera les majuscules et les accentués.Pourtant j’apprecie aussi le site.
Pour le banch avec strpos (preg_match(‘`^.*GoogleBot.*$`’,$var)) est tout greedy, il aurait peut être mieux valu (preg_match(‘`GoogleBot`’,$var)) environ 25% plus rapide.
http://lumadis.be/regex/test_regex.php?id=549
Ca aurait pas changé grand chose mais bon. Quand aux filtres de PHP, c’est vrai que ca valide un peu n’importe quoi. Bonne continuation, t’es bookmarké donc je te surveille..
about 1 year ago
Oui malheureusement Expreg n’est plus maintenu malgré un sursaut une année. Il n’est reste pas moins le meilleurs site pour comprendre les regex
Sinon oui ça n’aurait pas changé grand chose aux résultats exposés, les bonnes méthodes sont toujours les meilleurs
C’était le but de l’article.
Oula je suis surveillé je vais faire attention à ce que je dis :p. Sans rire peu d’articles mais normalement un article prochainement sur une étude sur la Géolocalisation par IP… En même temps ça fait 5 mois que je dois le sortir
about 1 year ago
You’re welcome!
about 1 year ago
Test à refaire avec la regex pour les mails dispo sur ce site : http://www.regular-expressions.info/email.html au paragraphe « The Official Standard: RFC 2822″
about 1 year ago
J’ai refait ce test de comparaison avec filter et preg_match pour la validation des adresses email.
Le constat reste le même : filter est plus lent que la vérification avec un preg_match.
Test refait avec les regex que tu présente dans ton lien (que je connais) et la regex que j’utilise personnellement qui fonctionne très bien qui est :
preg_match(‘`^(?!\.)[.\w!#$%&\'*+/=?^_\`{|}~-]+@(?!-)[\w-]+\.\w+$`i’, $email);
J’obtiens en gros les mêmes résultats que présenté dans l’article, entre les trois regex les temps d’exécution sont quasiment similaires.
about 1 year ago
Moi, j’ai un écart quasi du simple au double entre filter_var() et preg_match() sur 1 à 10000 itérations, ça se resserre aux alentours de 100 000 itérations (~80%), je suis pas allé au delà. Pour strpos() et preg_match quasi pas de différence sur 10000 itérations, légère différence en faveur de strpos() sur 100000. Bon, d’un autre côté, on s’y attendait un peu.
tests fait sous PEAR Benchmark.