Quel est le problème avec les articles populaires qui disent que foo est plus rapide que bar?

Note du traducteur: Je pensais aussi que le temps pour les articles était "Quoi de plus rapide - des guillemets doubles ou simples?" Il a fallu encore 10 ans. Mais ici, un article similaire ("Quelles astuces de performance fonctionnent réellement") a récemment recueilli une note relativement élevée sur Reddit et est même entré dans le condensé PHP sur Habré. En conséquence, j'ai décidé de traduire l'article avec une analyse critique de ces «tests» et d'autres similaires.


Il existe de nombreux articles (voire des sites entiers) consacrés au lancement de différents tests comparant les performances de différentes constructions syntaxiques et affirmant sur cette base que l'un est plus rapide que l'autre.


Problème principal


De tels tests sont incorrects pour de nombreuses raisons, allant de la question aux erreurs d'implémentation. Mais le plus important - de tels tests sont dénués de sens et en même temps nocifs.


  • Ils n'ont pas de sens car ils n'ont aucune valeur pratique. Aucun véritable projet n'a jamais été accéléré en utilisant les méthodes fournies dans ces articles. Ce n'est pas parce que les différences de syntaxe sont importantes pour les performances, mais pour le traitement des données.
  • Ils sont nuisibles car ils conduisent à l'apparition des superstitions les plus folles et - pire encore - encouragent les lecteurs sans méfiance à écrire du mauvais code, pensant qu'ils "l'optimisent".

Cela devrait suffire à clore la question. Mais même si vous acceptez les règles du jeu et prétendez que ces «tests» ont au moins un certain sens, il s'avère que leurs résultats ne se réduisent qu'à une démonstration du manque de connaissances du testeur et de son manque d'expérience.


Simple contre double


Prenez les citations notoires, "simple contre double". Bien sûr, aucune citation n'est plus rapide. Tout d'abord, il existe un cache opcode , qui stocke le résultat de l'analyse du script PHP dans le cache. Dans ce cas, le code PHP est enregistré au format opcode, où les mêmes littéraux de chaîne sont stockés en tant qu'entités absolument identiques, quelles que soient les guillemets utilisés dans le script PHP. Ce qui signifie l'absence même d'une différence théorique de performance.


Mais même si nous n'utilisons pas de cache d'opcode (bien que nous le devrions, si notre tâche est vraiment d'augmenter les performances), nous constaterons que la différence dans le code d'analyse est si petite (plusieurs transitions conditionnelles comparant des caractères à un octet, littéralement plusieurs instructions de processeur) qu'elle sera absolument indétectable. Cela signifie que tous les résultats obtenus ne présenteront que des problèmes dans l'environnement de test. Il y a un article très détaillé, Disproving the Single Quotes Performance Myth du développeur principal de PHP Nikita Popov, qui analyse ce problème en détail. Néanmoins, un testeur énergétique apparaît presque chaque mois pour révéler à la société une «différence» imaginaire de performance.


Incohérences logiques


Certains tests sont généralement dénués de sens, simplement du point de vue de la question: par exemple, le test intitulé "Le lancer est-il vraiment une opération super coûteuse?" c'est essentiellement la question "Est-ce vraiment que le traitement d'une erreur sera plus cher que le non-traitement?". Êtes-vous sérieux? Bien sûr, l'ajout de fonctionnalités de base au code le rendra «plus lent». Mais cela ne signifie pas que de nouvelles fonctionnalités ne doivent pas du tout être ajoutées, sous un prétexte aussi ridicule. Si vous parlez comme ça, alors le programme le plus rapide est celui qui ne fait rien du tout! Le programme doit être utile et fonctionner sans erreurs en premier lieu. Et ce n'est qu'après cela, et seulement s'il fonctionne lentement, qu'il doit être optimisé. Mais si la question elle-même n'a pas de sens, alors pourquoi s'embêter à tester les performances? C'est drôle que le testeur n'ait pas pu implémenter correctement même ce test insensé, qui sera montré dans la section suivante.


Ou un autre exemple, un test intitulé " $row[id] sera-t-il vraiment plus lent que $row['id'] ?" c'est essentiellement la question "Quel code est le plus rapide - celui qui fonctionne avec ou sans erreur?" (car écrire id sans guillemets dans ce cas est une erreur du niveau E_NOTICE , et une telle écriture sera déconseillée dans les futures versions de PHP). WTF? Quel est l'intérêt de mesurer généralement les performances d'un code d'erreur? L'erreur doit être corrigée simplement parce qu'il s'agit d'une erreur et non pas parce qu'elle ralentira l'exécution du code. C'est drôle que le testeur n'ait pas pu implémenter correctement même ce test insensé, qui sera montré dans la section suivante.


Test de qualité


Et encore - même un test sciemment inutile devrait être cohérent, cohérent - c'est-à-dire mesurer des valeurs comparables. Mais, en règle générale, ces tests sont effectués avec le talon gauche et, par conséquent, les résultats obtenus sont dénués de sens et ne sont pas pertinents pour la tâche.


Par exemple, notre stupide testeur a entrepris de mesurer "l'utilisation excessive de l'opérateur try..catch ". Mais dans le test actuel, il a mesuré non seulement le try catch , mais aussi le throw , lançant une exception à chaque itération de la boucle. Mais un tel test est tout simplement incorrect, car dans la réalité, des erreurs ne se produisent pas à chaque exécution de script.


Bien sûr, les tests ne doivent pas être effectués sur les versions bêta de PHP et ne doivent pas comparer les solutions traditionnelles avec des solutions expérimentales. Et si le testeur s'engage à comparer la "vitesse d'analyse de json et xml", il ne doit pas utiliser la fonction expérimentale dans les tests.


Certains tests démontrent simplement un malentendu complet par le testeur de la tâche qu'il a définie. Un exemple similaire tiré d'un article récemment publié a déjà été mentionné ci-dessus: l'auteur du test a essayé de savoir si le code à l'origine de l'erreur ("Utilisation d'une constante non définie") serait plus lent que le code sans erreur (qui utilise un littéral de chaîne syntaxiquement correct), mais a échoué même avec ce test manifestement dénué de sens, en comparant les performances d'un nombre cité avec les performances d'un nombre écrit sans guillemets. Bien sûr, vous pouvez écrire des nombres sans guillemets en PHP (contrairement aux chaînes), et en conséquence, l'auteur a testé une fonctionnalité complètement différente, recevant des résultats incorrects.


Il y a d'autres problèmes à considérer, tels que l'environnement de test. Il existe des extensions de PHP telles que XDebug qui peuvent avoir un très gros impact sur les résultats des tests. Ou le cache d'opcode déjà mentionné, qui doit être inclus lors des tests de performances afin que les résultats des tests puissent avoir au moins un certain sens.


La manière dont les tests sont effectués est également importante. Étant donné que le processus PHP meurt entièrement après chaque demande, il est logique de tester les performances de l'ensemble du cycle de vie, en commençant par la création d'une connexion à un serveur Web et en terminant par la fermeture de cette connexion. Il existe des utilitaires comme Apache benchmark ou Siege qui vous permettent de le faire.


Amélioration réelle des performances


Tout cela est bien, mais quelle conclusion le lecteur doit-il tirer de cet article? Quels tests de performances sont inutiles par définition? Bien sûr que non. Mais ce qui compte vraiment, c'est la raison pour laquelle ils devraient commencer. Les tests à partir de zéro sont une perte de temps. Il doit toujours y avoir une raison spécifique pour exécuter des tests de performances. Et cette raison est appelée «profilage» . Lorsque votre application commence à s'exécuter lentement, vous devez effectuer un profilage, ce qui signifie mesurer la vitesse de différentes sections de code pour trouver la plus lente. Après avoir trouvé un tel site, nous devons déterminer la cause. Le plus souvent, il s'agit soit d'un volume beaucoup plus important que nécessaire, de la quantité de données traitées ou d'une demande adressée à une source de données externe. Pour le premier cas, l'optimisation consistera à réduire la quantité de données traitées, et pour le second cas, à mettre en cache les résultats de la requête.


Par exemple, en termes de performances, cela ne fait aucune différence si nous utilisons une boucle explicitement prescrite ou la fonction PHP intégrée pour traiter les tableaux (qui ne sont essentiellement que du sucre syntaxique). Ce qui est vraiment important, c'est la quantité de données que nous transmettons pour le traitement. S'il est déraisonnablement grand, nous devons le couper ou déplacer le traitement ailleurs (vers la base de données). Cela nous donnera une énorme amélioration des performances qui sera réelle . Il est peu probable que la différence entre les méthodes d'appel de la boucle pour le traitement des données soit perceptible.


Ce n'est qu'après avoir effectué ces améliorations de performances obligatoires, ou si nous ne pouvons pas réduire la quantité de données traitées, que nous pouvons lancer des tests de performances. Mais là encore, ces tests ne doivent pas être effectués à partir de zéro. Afin de commencer à comparer les performances d'une boucle explicite et d'une fonction en ligne, nous devons être sûrs que la boucle est la cause du problème, pas son contenu (spoiler: bien sûr, c'est le contenu).


Un exemple récent de ma pratique: dans le code, il y avait une requête utilisant Doctrine Query Builder, qui devait prendre plusieurs milliers de paramètres. La requête elle-même est assez rapide, mais Doctrine met un certain temps à digérer plusieurs milliers de paramètres. En conséquence, la requête a été réécrite en SQL pur et les paramètres ont été transférés vers la méthode execute () de la bibliothèque PDO, qui gère presque autant de paramètres presque instantanément.


Est-ce à dire que je n'utiliserai jamais Doctrine Query Builder? Bien sûr que non. Il est parfait pour 99% des tâches, et je continuerai à l'utiliser pour toutes les requêtes. Et seulement dans des cas exceptionnels, cela vaut la peine d'utiliser une méthode moins pratique, mais plus productive.


La requête et les paramètres de cette sélection ont été construits en boucle. Si j'avais une idée stupide de gérer la façon dont le cycle est appelé, je perdrais simplement du temps sans résultat positif. Et c'est l'essence même de toutes les optimisations de performances: pour optimiser uniquement le code qui s'exécute lentement dans votre cas particulier. Et pas le code qui était considéré comme lent il y a longtemps, dans une galaxie lointaine, lointaine, ou le code que quelqu'un a appelé lent en se basant sur des tests dénués de sens.

Source: https://habr.com/ru/post/fr419743/


All Articles