144 votes

Pourquoi le require_once est si mal utiliser ?

Tout ce que j’ai lu sur PHP mieux garde disant n’utilise pas les pratiques de codage `` à cause de la vitesse.

Pourquoi est-ce ?

Quelle est la façon propre/mieux de faire la même chose que `` ? Si c’est important, je suis en utilisant PHP5.

148voto

Edward Z. Yang Points 13760

Ce fil me fait grincer des dents, parce qu'il y a déjà une "solution posté", et c'est, à toutes fins et intentions, de mal. Nous allons énumérer:

  1. Définit sont vraiment cher en PHP. Vous pouvez chercher ou tester vous-même, mais le seul moyen efficace de définir une constante globale en PHP via une extension. (Les constantes de classe sont en fait assez décent performance sage, mais c'est un point discutable, en raison de 2)

  2. Si vous utilisez require_once() de façon appropriée, qui est, pour l'inclusion de classes, vous n'avez même pas besoin d'un définir; il suffit de vérifier si class_exists('Classname'). Si le fichier que vous y compris contient le code, c'est à dire que vous l'utilisez dans la procédure de la mode, il n'y a absolument aucune raison pour qu' require_once() devrait être nécessaire pour vous; chaque fois que vous incluez le fichier que vous avez la prétention de faire un appel de sous-routine.

Donc, pour un certain temps, beaucoup de gens ont fait usage de l' class_exists() méthode pour leurs inclusions. Je ne l'aime pas parce que c'est moche, mais ils ont une bonne raison de: require_once() a été assez inefficace avant certaines des plus récentes versions de PHP. Mais ce qui a été fixé, et il est ma conviction que le supplément de bytecode, vous auriez à le compiler pour le conditionnel, et l'appel de la méthode, serait de loin surpondérer internes table de hachage vérifier.

Maintenant, un aveu: ce genre de choses est difficile à tester, car il tient compte pour si peu de temps d'exécution.

Voici la question que vous devez penser à: comprend, en règle générale, sont coûteux en PHP, parce que chaque fois que l'interprète des hits de l'un, c'est pour revenir en analyser mode, de générer les opcodes, puis revenir en arrière. Si vous avez un 100+ comprend, cela va certainement avoir un impact sur les performances. La raison pour laquelle l'utilisation ou la non utilisation de require_once est une question importante, car elle rend la vie difficile pour les caches d'opcode. Une explication peut être trouvée ici, mais ce que cela se résume à cela:

  • Si, au cours de l'analyse, vous savez exactement ce que comprennent les fichiers dont vous aurez besoin pour l'ensemble de la durée de vie de la demande, require() de ceux du début et le cache opcode occupons de tout le reste pour vous.

  • Si vous n'exécutez pas un cache d'opcode, vous êtes dans une situation difficile. Inline tous vos comprend en un seul fichier (ne pas le faire au cours du développement, seulement dans la production) peut certainement aider à l'analyse, mais c'est une douleur à faire, et aussi, vous devez savoir exactement ce que vous serez notamment lors de la demande.

  • Chargement automatique est très pratique, mais lente, pour la raison que l'autoload logique doit être exécutée à chaque fois un include est fait. Dans la pratique, j'ai trouvé que le chargement automatique de plusieurs dossiers spécialisés pour une demande ne cause pas trop de problème, mais vous ne devriez pas être auto-chargement de tous les fichiers dont vous aurez besoin.

  • Si vous avez peut-être 10 comprend (c'est un très dos de l'enveloppe de calcul), tout cela en se branlant n'est pas la peine: il suffit d'optimiser vos requêtes de base de données ou quelque chose.

115voto

Oli Points 65050

Require_once et include_once exiger que le système garde une trace de ce qui a déjà été inclus/nécessaire et chaque fois qu'il y a un autre include/require-_once déclaration, ils doivent vérifier que le journal.

Sur une base de calcul, je peux voir qu'il y a du travail supplémentaire et les ressources va en faire, mais assez détriment de la vitesse de l'ensemble de l'application? Je doute vraiment... Non, sauf si vous êtes vraiment vieux matériel.

Si vous êtes vraiment intéressé à ce sujet, l'alternative est de faire le travail vous-même dans le plus léger, d'une manière que vous pouvez sortir avec. Pour des applications simples, juste pour s'assurer que vous avez inclus seulement une fois devrait suffire, mais si vous êtes encore en train de redéfinir les erreurs, vous pouvez quelque chose comme ceci:

if (!defined('MyIncludeName')) {
    require('MyIncludeName');
    define('MyIncludeName', 1);
}

Ce n'est pas grand et il va indésirable de votre code, mais c'est la lumière. Personnellement, je vais rester avec l' ..._once des déclarations.

68voto

terson Points 832

Je suis curieuse et vérifié Adam Backstrom lien à Tech de Votre Univers. Cet article décrit l'une des raisons qui nécessitent doit être utilisé au lieu de require_once. Cependant, leurs revendications ne tient pas à mon analyse. Je serais curieux de voir où j'ai peut-être misanalysed la solution. J'ai utilisé php 5.2.0 pour les comparaisons.

J'ai commencé par la création de 100 fichiers d'en-tête utilisés require_once d'inclure un autre fichier d'en-tête. Chacun de ces fichiers cherché quelque chose comme:

<?php
// /home/fbarnes/phpperf/hdr0.php
require_once "../phpperf/common_hdr.php";

?>

J'ai créé à l'aide d'un rapide bash hack:

for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
  echo "<?php
// $i" > $i
  cat helper.php >> $i;
done

De cette façon, je pouvais facilement permuter entre l'utilisation de require_once et besoin lorsque vous y compris les fichiers d'en-tête. Ensuite, j'ai créé un app.php pour charger une centaine de dossiers. Cela ressemblait à:

<?php

// Load all of the php hdrs that were created previously
for($i=0; $i < 100; $i++)
{
  require_once "/home/fbarnes/phpperf/hdr$i.php";
}

// Read the /proc file system to get some simple stats
$pid = getmypid();
$fp = fopen("/proc/$pid/stat", "r");
$line = fread($fp, 2048);
$array = split(" ", $line);

// write out the statistics; on RedHat 4.5 w/ kernel 2.6.9
// 14 is user jiffies; 15 is system jiffies
$cntr = 0;
foreach($array as $elem)
{
  $cntr++;
  echo "stat[$cntr]: $elem\n";
}
fclose($fp);

?>

J'ai comparé le require_once les en-têtes de avec des besoin des en-têtes utilisé un fichier d'en-tête qui ressemble à:

<?php
// /home/fbarnes/phpperf/h/hdr0.php
if(!defined('CommonHdr'))
{
  require "../phpperf/common_hdr.php";
  define('CommonHdr', 1);
}

?>

Je n'ai pas trouvé beaucoup de différence lors de l'exécution de cette exigent vs require_once. En fait mes premiers tests semblent sous-entendre que require_once a été légèrement plus rapide, mais je n'y crois pas nécessairement. J'ai répété l'expérience avec 10000 fichiers d'entrée. Ici, j'ai fait voir une différence cohérente. J'ai couru le test plusieurs fois, les résultats sont proches mais à l'aide de require_once utilise en moyenne de 30,8 utilisateur jiffies et 72,6 système de jiffies; l'aide d'exiger utilise en moyenne de 39,4 utilisateur jiffies et 72,0 système de jiffies. Par conséquent, il apparaît que la charge est légèrement plus faible en utilisant require_once. Cependant, l'horloge du mur est légèrement augmenté. Les 10 000 require_once utilisation d'appels de 10 h 15 secondes en moyenne et 10 000 exiger l'utilisation d'appels de 9,84 secondes en moyenne.

La prochaine étape est de se pencher sur ces différences. J'ai utilisé strace pour analyser le système des appels qui sont faits.

Avant l'ouverture d'un fichier à partir de require_once le système suivant les appels sont effectués:

time(NULL)                              = 1223772434
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
time(NULL)                              = 1223772434
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

Cela contraste avec l'exigent:

time(NULL)                              = 1223772905
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
time(NULL)                              = 1223772905
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

Tech Votre Univers implique que require_once doit faire plus d'lstat64 appels. Cependant, ils font tous deux le même nombre de lstat64 appels. Éventuellement, la différence est que je ne suis pas candidat de l'APC d'optimiser le code ci-dessus. Cependant, la prochaine chose que j'ai faite a été de comparer la sortie de strace pour l'ensemble de l'exécute:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
  190709 strace_1000r.out
  210707 strace_1000ro.out
  401416 total

Effectivement, il y a environ deux appels système par fichier d'en-tête lors de l'utilisation de require_once. Une différence est que le require_once a un appel supplémentaire à la fonction time ():

[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out 
strace_1000r.out:20009
strace_1000ro.out:30008

L'autre appel système est getcwd():

[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out 
strace_1000r.out:5
strace_1000ro.out:10004

Cela s'appelle parce que j'ai décidé de chemin d'accès relatif référencé dans la hdrXXX fichiers. Si je fais ce une référence absolue, la seule différence est le temps supplémentaire(NULL) appel dans le code:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out 
  190705 strace_1000r.out
  200705 strace_1000ro.out
  391410 total
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20008
strace_1000ro.out:30008

Cela semble impliquer que vous pourriez réduire le nombre d'appels à l'aide des chemins absolus plutôt que des chemins relatifs. La seule différence, en dehors de qui est le temps(NULL) appels qui semblent être utilisés pour instrumentant le code de comparer ce qui est plus rapide.

Un autre point à souligner est que l'APC d'optimisation paquet a une option appelée "apc.include_once_override" qui prétend qu'elle réduit le nombre d'appels système effectués par le require_once et include_once appels (voir le PHP docs).

Désolé pour le long post. Je me suis curieux.

21voto

nickf Points 185423

Pouvez-vous nous donner les liens vers ces pratiques de codage qui disent l'éviter? Pour autant que je suis concerné, c'est un non-problème. Je n'ai pas regardé le code source de moi-même, mais j'imagine que la seule différence entre include et include_once que include_once ajoute que le nom de fichier d'un tableau et vérifie sur le tableau à chaque fois. Il serait facile de garder ce tableau trié, de sorte que la recherche de plus il convient de O(log n), et même un moyen largish demande seulement une couple de douzaine comprend.

7voto

Greg Points 132247

Une meilleure façon de faire les choses est d'utiliser une approche orientée objet et d'utiliser __autoload () .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X