Tout ce que j'ai lu sur les meilleures pratiques de codage PHP me dit de ne pas utiliser à require_once
cause de la vitesse.
Pourquoi est-ce?
Quelle est la bonne / meilleure façon de faire la même chose que require_once
? Si cela compte, j'utilise PHP 5.
php
performance
require-once
Uberfuzzy
la source
la source
Réponses:
require_once
et lesinclude_once
deux exigent que le système garde un journal de ce qui a déjà été inclus / requis. Chaque*_once
appel signifie vérifier ce journal. Il y a donc certainement un travail supplémentaire en cours, mais suffisamment pour nuire à la vitesse de l'ensemble de l'application?... J'en doute vraiment ... Sauf si vous utilisez du matériel vraiment ancien ou que vous le faites beaucoup .
Si vous êtes en train de faire des milliers de
*_once
, vous pouvez faire le travail vous - même d'une façon plus légère. Pour des applications simples, tout en vous assurant que vous avez seulement inclus une fois devrait suffire , mais si vous obtenez toujours des erreurs de Redéfinir, vous pourriez quelque chose comme ceci:Je m'en tiendrai personnellement aux
*_once
déclarations, mais sur un benchmark stupide d'un million de passes, vous pouvez voir une différence entre les deux:10-100 × plus lent avec
require_once
et il est curieux que celarequire_once
soit apparemment plus lenthhvm
. Encore une fois, cela n'est pertinent pour votre code que si vous exécutez des*_once
milliers de fois.la source
Ce fil me fait grincer des dents, car il y a déjà eu une "solution publiée", et c'est, à toutes fins utiles, faux. Énumérons:
Les définitions sont vraiment chères en PHP. Vous pouvez le rechercher ou le tester vous-même, mais le seul moyen efficace de définir une constante globale en PHP est via une extension. (Les constantes de classe sont en fait assez bonnes en termes de performances, mais c'est un point discutable, à cause de 2)
Si vous utilisez
require_once()
correctement, c'est-à-dire pour l'inclusion de classes, vous n'avez même pas besoin de définir; il suffit de vérifier siclass_exists('Classname')
. Si le fichier que vous incluez contient du code, c'est-à-dire que vous l'utilisez de manière procédurale, il n'y a absolument aucune raison quirequire_once()
devrait être nécessaire pour vous; à chaque fois que vous incluez le fichier que vous supposez faire un appel de sous-programme.Donc, pendant un certain temps, beaucoup de gens ont utilisé la
class_exists()
méthode pour leurs inclusions. Je n'aime pas ça parce que c'est déroutant, mais ils avaient de bonnes raisons de le faire:require_once()
c'était assez inefficace avant certaines des versions les plus récentes de PHP. Mais cela a été corrigé, et je soutiens que le bytecode supplémentaire que vous auriez à compiler pour le conditionnel, et l'appel de méthode supplémentaire, l'emporteraient de loin sur toute vérification interne de la table de hachage.Maintenant, un aveu: ce truc est difficile à tester, car il ne représente que si peu de temps d'exécution.
Voici la question à laquelle vous devriez réfléchir: les inclusions, en règle générale, sont chères en PHP, car chaque fois que l'interpréteur en frappe un, il doit repasser en mode d'analyse, générer les opcodes, puis revenir en arrière. Si vous avez plus de 100 inclusions, cela aura certainement un impact sur les performances. La raison pour laquelle l'utilisation ou non de require_once est une question si importante est que cela rend la vie difficile pour les caches d'opcode. Une explication à cela peut être trouvée ici, mais cela se résume à ceci:
Si pendant l'analyse, vous savez exactement de quels fichiers d'inclusion vous aurez besoin pendant toute la durée de vie de la requête,
require()
ceux du tout début et le cache d'opcode gérera tout le reste pour vous.Si vous n'exécutez pas de cache opcode, vous êtes dans une situation difficile. L'intégration de toutes vos inclusions dans un seul fichier (ne le faites pas pendant le développement, uniquement en production) peut certainement aider à analyser le temps, mais c'est difficile à faire, et aussi, vous devez savoir exactement ce que vous allez inclure pendant le demande.
Le chargement automatique est très pratique, mais lent, car la logique de chargement automatique doit être exécutée chaque fois qu'une inclusion est effectuée. En pratique, j'ai constaté que le chargement automatique de plusieurs fichiers spécialisés pour une demande ne posait pas trop de problème, mais vous ne devriez pas charger automatiquement tous les fichiers dont vous aurez besoin.
Si vous avez peut-être 10 inclus (c'est un calcul très arrière du calcul de l'enveloppe), tout ce branlage n'en vaut pas la peine: optimisez simplement vos requêtes de base de données ou quelque chose du genre.
la source
define()
,require_once()
etdefined()
tous prennent environ 1 à 2 microsecondes chacun sur ma machine.Je suis devenu curieux et j'ai vérifié le lien d'Adam Backstrom vers Tech Your Universe . Cet article décrit l'une des raisons pour lesquelles require doit être utilisé au lieu de require_once. Cependant, leurs affirmations ne tiennent pas compte de mon analyse. Je serais intéressé de voir où j'ai peut-être mal analysé la solution. J'ai utilisé PHP 5.2.0 pour les comparaisons.
J'ai commencé par créer 100 fichiers d'en-tête qui utilisaient require_once pour inclure un autre fichier d'en-tête. Chacun de ces fichiers ressemblait à quelque chose comme:
J'ai créé ceux-ci en utilisant un hack rapide Bash:
De cette façon, je pourrais facilement permuter entre l'utilisation de require_once et de require lors de l'inclusion des fichiers d'en-tête. J'ai ensuite créé un app.php pour charger les cent fichiers. Cela ressemblait à:
J'ai comparé les en-têtes require_once avec des en-têtes require qui utilisaient un fichier d'en-tête ressemblant à:
Je n'ai pas trouvé beaucoup de différence lors de l'exécution avec require et require_once. En fait, mes tests initiaux semblaient impliquer que require_once était légèrement plus rapide, mais je ne le crois pas nécessairement. J'ai répété l'expérience avec 10000 fichiers d'entrée. Ici, j'ai vu une différence cohérente. J'ai exécuté le test plusieurs fois, les résultats sont proches mais l'utilisation de require_once utilise en moyenne 30,8 jiffies utilisateur et 72,6 jiffies système; l'utilisation de require utilise en moyenne 39,4 jiffies utilisateur et 72,0 jiffies système. Par conséquent, il semble que la charge soit légèrement inférieure en utilisant require_once. Cependant, l'heure de l'horloge murale est légèrement augmentée. Les 10 000 appels require_once utilisent en moyenne 10,15 secondes et 10 000 appels nécessitent 9,84 secondes en moyenne.
L'étape suivante consiste à examiner ces différences. J'ai utilisé strace pour analyser les appels système en cours.
Avant d'ouvrir un fichier depuis require_once, les appels système suivants sont effectués:
Cela contraste avec exiger:
Tech Your Universe implique que require_once devrait faire plus d'appels lstat64. Cependant, ils effectuent tous les deux le même nombre d'appels lstat64. La différence est peut-être que je n'utilise pas APC pour optimiser le code ci-dessus. Cependant, j'ai ensuite comparé la sortie de strace pour toutes les exécutions:
En fait, il y a environ deux appels système supplémentaires par fichier d'en-tête lors de l'utilisation de require_once. Une différence est que require_once a un appel supplémentaire à la fonction time ():
L'autre appel système est getcwd ():
Ceci est appelé parce que j'ai décidé du chemin relatif référencé dans les fichiers hdrXXX. Si j'en fais une référence absolue, la seule différence est l'appel de temps supplémentaire (NULL) effectué dans le code:
Cela semble impliquer que vous pouvez réduire le nombre d'appels système en utilisant des chemins absolus plutôt que des chemins relatifs. La seule différence en dehors de cela est les appels de temps (NULL) qui semblent être utilisés pour instrumenter le code pour comparer ce qui est plus rapide.
Une autre note est que le package d'optimisation APC a une option appelée "apc.include_once_override" qui prétend réduire le nombre d'appels système effectués par les appels require_once et include_once (voir la documentation PHP ).
la source
Pouvez-vous nous donner des liens vers ces pratiques de codage qui disent de l'éviter? En ce qui me concerne, c'est un non-problème complet . Je n'ai pas regardé le code source moi-même, mais j'imagine que la seule différence entre
include
etinclude_once
est que celainclude_once
ajoute ce nom de fichier à un tableau et vérifie le tableau à chaque fois. Il serait facile de garder ce tableau trié, donc la recherche devrait être O (log n), et même une application de taille moyenne n'aurait que quelques douzaines d'inclusions.la source
Une meilleure façon de faire les choses est d'utiliser une approche orientée objet et d'utiliser __autoload () .
la source
__autoload()
est déconseillé et il pourrait être obsolète à l'avenir, vous devriez utiliserspl_autoload_register(...)
ces jours-ci ... PS2: ne vous méprenez pas, j'utilise parfois la fonctionnalité de chargement automatique; )Ce n'est pas l'utilisation de la fonction qui est mauvaise. C'est une mauvaise compréhension de comment et quand l'utiliser, dans une base de code globale. Je vais juste ajouter un peu plus de contexte à cette notion peut-être mal comprise:
Les gens ne devraient pas penser que require_once est une fonction lente. Vous devez inclure votre code d'une manière ou d'une autre.
require_once()
vsrequire()
la vitesse n'est pas le problème. Il s'agit de la performance qui entrave les mises en garde qui peuvent résulter de son utilisation aveugle. S'il est utilisé largement sans tenir compte du contexte, il peut entraîner un énorme gaspillage de mémoire ou un gaspillage de code.Ce que j'ai vu qui est vraiment mauvais, c'est quand d'énormes frameworks monolithiques sont utilisés
require_once()
de toutes les mauvaises manières, en particulier dans un environnement complexe orienté objet (OO).Prenons l'exemple de l'utilisation
require_once()
en haut de chaque classe comme on le voit dans de nombreuses bibliothèques:La
User
classe est donc conçue pour utiliser les trois autres classes. C'est suffisant!Mais maintenant, que se passe-t-il si un visiteur navigue sur le site et n'est même pas connecté et que le framework se charge:
require_once("includes/user.php");
pour chaque demande.Il comprend 1 + 3 classes inutiles qu'il n'utilisera jamais lors de cette demande particulière. C'est ainsi que les frameworks gonflés finissent par utiliser 40 Mo par requête au lieu de 5 Mo ou moins.
Les autres façons dont il peut être mal utilisé, c'est lorsqu'une classe est réutilisée par beaucoup d'autres! Disons que vous avez environ 50 classes qui utilisent des
helper
fonctions. Pour vous assurer quehelpers
ces classes sont disponibles lors de leur chargement, vous obtenez:Il n'y a rien de mal ici en soi. Cependant, si une demande de page inclut 15 classes similaires. Vous exécutez
require_once
15 fois, ou pour un joli visuel:L'utilisation de require_once () affecte techniquement les performances pour exécuter cette fonction 14 fois, en plus d'avoir à analyser ces lignes inutiles. Avec seulement 10 autres classes très utilisées avec ce problème similaire, cela pourrait représenter plus de 100 lignes d'un tel code répétitif plutôt inutile.
Avec cela, cela vaut probablement la peine d'être utilisé
require("includes/helpers.php");
au démarrage de votre application ou de votre framework. Mais comme tout est relatif, tout dépend si le poids par rapport à la fréquence d'utilisation de lahelpers
classe vaut la peine d'économiser 15 à 100 lignesrequire_once()
. Mais si la probabilité de ne pas utiliser lehelpers
fichier sur une demande donnée est nulle, alorsrequire
devrait certainement être dans votre classe principale à la place. Avoirrequire_once
dans chaque classe séparément devient un gaspillage de ressources.La
require_once
fonction est utile lorsque cela est nécessaire, mais elle ne doit pas être considérée comme une solution monolithique à utiliser partout pour charger toutes les classes.la source
Le wiki PEAR2 (quand il existait) avait l'habitude de lister les bonnes raisons d'abandonner toutes les directives require / include en faveur du chargement automatique , au moins pour le code de la bibliothèque. Celles-ci vous attachent à des structures de répertoires rigides lorsque des modèles d'emballage alternatifs comme phar sont à l'horizon.
Mise à jour: Comme la version Web archivée du wiki est horriblement moche, j'ai copié les raisons les plus convaincantes ci-dessous:
la source
Les
*_once()
fonctions indiquent chaque répertoire parent pour garantir que le fichier que vous incluez n'est pas le même que celui qui a déjà été inclus. C'est en partie la raison du ralentissement.Je recommande d'utiliser un outil comme Siege pour l'analyse comparative. Vous pouvez essayer toutes les méthodologies suggérées et comparer les temps de réponse.
Plus sur le sujet
require_once()
est à Tech Your Universe .la source
Même si
require_once
etinclude_once
sont plus lents querequire
etinclude
(ou quelles que soient les alternatives), nous parlons ici du plus petit niveau de micro-optimisation. Votre temps est bien mieux dépensé pour optimiser cette boucle ou requête de base de données mal écrite que de vous soucier de quelque chose commerequire_once
.Maintenant, on pourrait faire un argument en disant que cela
require_once
permet de mauvaises pratiques de codage parce que vous n'avez pas besoin de faire attention à garder vos inclusions propres et organisées, mais cela n'a rien à voir avec la fonction elle - même et surtout pas sa vitesse.Évidemment, le chargement automatique est préférable pour des raisons de propreté du code et de facilité de maintenance, mais je tiens à préciser que cela n'a rien à voir avec la vitesse .
la source
Vous testez, en utilisant include, l'alternative d'oli et __autoload (); et testez-le avec quelque chose comme APC installé.
Je doute que l'utilisation de la constante accélérera les choses.
la source
Oui, c'est un peu plus cher que le simple besoin (). Je pense que si vous pouvez garder votre code suffisamment organisé pour ne pas dupliquer les inclusions, n'utilisez pas les fonctions * _once (), car cela vous fera gagner quelques cycles.
Mais l'utilisation des fonctions _once () ne va pas tuer votre application. En gros, ne l'utilisez pas comme excuse pour ne pas avoir à organiser vos inclusions . Dans certains cas, son utilisation est toujours inévitable, et ce n'est pas un gros problème.
la source
Je pense que dans la documentation PEAR, il y a une recommandation pour require, require_once, include et include_once. Je suis cette directive. Votre candidature serait plus claire.
la source
Cela n'a rien à voir avec la vitesse. Il s'agit d'échouer gracieusement.
Si require_once () échoue, votre script est terminé. Rien d'autre n'est traité. Si vous utilisez include_once (), le reste de votre script essaiera de continuer à être rendu, de sorte que vos utilisateurs ne seraient potentiellement pas plus avisés de quelque chose qui a échoué dans votre script.
la source
Mon opinion personnelle est que l'utilisation de require_once (ou include_once) est une mauvaise pratique car require_once vérifie pour vous si vous avez déjà inclus ce fichier et supprime les erreurs des fichiers double inclus entraînant des erreurs fatales (comme la déclaration en double de fonctions / classes / etc.) .
Vous devez savoir si vous devez inclure un fichier.
la source