performance-classloading.xml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 24249 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="performance.classloading">
  5. <title>Chargement des classes</title>
  6. <para>
  7. Tous ceux qui ont déjà réalisé le profilage d'une application Zend Framework
  8. reconnaîtront immédiatement que le chargement des classes y est relativement coûteux. Entre
  9. le nombre important de fichier de classe qui doivent être chargées pour un grand nombre de
  10. composants et l'utilisation des plugins qui n'impliquent pas une relation 1:1 entre leur nom
  11. de classe et le système de fichier, les différents appels de <code>include_once</code> et
  12. <code>require_once</code> peuvent être problématique. Ce chapitre a pour but de fournir des
  13. solutions concrètes pour solutionner ces problèmes.
  14. </para>
  15. <sect2 id="performance.classloading.includepath">
  16. <title>Comment optimiser mon include_path&#160;?</title>
  17. <para>
  18. Une optimisation triviale pour accélérer la vitesse de chargement des classes est
  19. de faire attention à votre <code>include_path</code>. En particulier, vous devriez faire
  20. quatre choses&#160;: utilisez des chemins absolus (ou des chemins relatifs à des chemins
  21. absolus), réduire le nombre des chemins à inclure, définir le dossier de Zend Framework
  22. le plus tôt possible dans l'<code>include_path</code> et inclure le dossier courant en
  23. dernier dans votre <code>include_path</code>.
  24. </para>
  25. <sect3 id="performance.classloading.includepath.abspath">
  26. <title>Utiliser des chemins absolus</title>
  27. <para>
  28. Tandis que ceci peut sembler une micro-optimisation, le fait est que si vous
  29. ne le faites pas, vous n'obtiendrez qu'un très petit avantage de la mise en cache du
  30. realpath de PHP, et en conséquence, le cache d'opcode ne fonctionnera pas tout à
  31. fait comme vous pourriez l'imaginer.
  32. </para>
  33. <para>
  34. Il y a deux manières simples de s'assurer de ceci. Premièrement, vous pouvez
  35. le mettre en dur dans votre <filename>php.ini</filename>,
  36. <filename>httpd.conf</filename>, ou <filename>.htaccess</filename>. Deuxièmement,
  37. vous pouvez utiliser la fonction <code>realpath()</code> de PHP au moment du
  38. paramétrage de votre <code>include_path</code>&#160;:
  39. </para>
  40. <programlisting language="php"><![CDATA[
  41. $paths = array(
  42. realpath(dirname(__FILE__) . '/../library'),
  43. '.',
  44. );
  45. set_include_path(implode(PATH_SEPARATOR, $paths);
  46. ]]></programlisting>
  47. <para>
  48. Vous <emphasis>pouvez</emphasis> utiliser des chemins relatifs - du moment
  49. qu'ils sont relatifs à un chemin absolu&#160;:
  50. </para>
  51. <programlisting language="php"><![CDATA[
  52. define('APPLICATION_PATH', realpath(dirname(__FILE__)));
  53. $paths = array(
  54. APPLICATION_PATH . '/../library'),
  55. '.',
  56. );
  57. set_include_path(implode(PATH_SEPARATOR, $paths);
  58. ]]></programlisting>
  59. <para>
  60. Néanmoins, c'est typiquement une tâche insignifiante de fournir simplement le
  61. chemin à <code>realpath()</code>.
  62. </para>
  63. </sect3>
  64. <sect3 id="performance.classloading.includepath.reduce">
  65. <title>Réduire le nombre de dossier défini dans l'include_path</title>
  66. <para>
  67. Les chemins d'inclusion sont scannés dans l'ordre dans lequel ils apparaissent
  68. dans l'<code>include_path</code>. Évidemment, ceci veut dire que vous aurez un
  69. résultat plus rapide si le fichier est trouvé dans le premier chemin scanné que si
  70. vous le trouvez dans le dernier chemin scanné. De plus, une amélioration plutôt
  71. évidente est de diminuer tout simplement le nombre de chemins dans votre
  72. <code>include_path</code> à seulement de ce que vous avez réellement besoin.
  73. Regardez chaque chemin que vous avez défini dans votre include_path pour déterminer
  74. si vous avez réellement besoin d'une fonctionnalité dans votre application&#160;; si
  75. ce n'est pas le cas, enlevez le.
  76. </para>
  77. <para>
  78. Une autre optimisation consiste en la combinaison de chemins. Par exemple,
  79. Zend Framework suit la convention de nommage PEAR&#160;; ainsi , si vous utilisez
  80. des librairies PEAR (ou d'autres framework ou librairies de composants qui
  81. respectent la convention de nommage PEAR), essayez de mettre toutes ces librairies
  82. dans le même chemin de l'<code>include_path</code>. Ceci peut souvent être réalisé
  83. par quelque chose d'assez simple comme de créer des liens symboliques vers une ou
  84. plusieurs bibliothèques dans un dossier commun.
  85. </para>
  86. </sect3>
  87. <sect3 id="performance.classloading.includepath.early">
  88. <title>
  89. Définir le dossier de Zend Framework le plus tôt possible dans l'include_path
  90. </title>
  91. <para>
  92. Pour continuer avec les suggestions précédentes, une autre optimisation
  93. évidente est de définir le dossier de Zend Framework le plus tôt possible dans votre
  94. <code>include_path</code>. Dans la plupart des cas, il devrait être le premier de la
  95. liste. Ceci permet de s'assurer les fichiers de Zend Framework à inclure le sont dès
  96. le premier scan.
  97. </para>
  98. </sect3>
  99. <sect3 id="performance.classloading.includepath.currentdir">
  100. <title>Définir le dossier courant le plus tard possible ou pas du tout</title>
  101. <para>
  102. La plupart des exemples d'<code>include_path</code> montre l'utilisation du
  103. répertoire courant ("<code>.</code>"). Ceci est pratique pour s'assurer que les
  104. scripts dans le même dossier que le fichier courant peuvent aussi être chargés.
  105. Cependant ces mêmes exemples montrent souvent ce dossier comme étant le premier de
  106. la liste des include_path - ce qui veut dire l'arbre de dossiers courant est
  107. toujours scanné en premier. La plupart du temps, avec Zend Framework, ce n'est pas
  108. nécessaire, et ce dossier peut tout naturellement être mis en dernière position de
  109. la liste.
  110. </para>
  111. <example id="performance.classloading.includepath.example">
  112. <title>Exemple : optimisation de l'include_path</title>
  113. <para>
  114. Essayons de mettre ensemble toutes ces suggestions. Considérons que nous
  115. utilisons une ou plusieurs composants PEAR en conjonction avec Zend Framework -
  116. par exemple les composants PHPUnit et Archive_Tar - et qu'il est
  117. occasionnellement nécessaire d'inclure les fichiers relativement au fichier
  118. courant.
  119. </para>
  120. <para>
  121. Premièrement, nous allons créer un dossier pour les librairies dans notre
  122. projet. Dans ce même dossier, nous allons créer un lien symbolique vers notre
  123. dossier Zend Framework "<filename>library/Zend</filename>", ainsi que les
  124. dossiers nécessaires dans notre installation PEAR&#160;:
  125. </para>
  126. <programlisting language="php"><![CDATA[
  127. library
  128. Archive/
  129. PEAR/
  130. PHPUnit/
  131. Zend/
  132. ]]></programlisting>
  133. <para>
  134. Ceci nous permet d'ajouter notre propre librairie si nécessaire, tout en
  135. laissant intact les librairies partagées.
  136. </para>
  137. <para>
  138. Ensuite, nous optons pur la création de notre <code>include_path</code>
  139. par programme à l'intérieur de notre fichier
  140. <filename>public/index.php</filename>. Ceci nous permet de déplacer notre code
  141. dans le système de fichiers, sans devoir éditer l'<code>include_path</code> à
  142. chaque fois.
  143. </para>
  144. <para>
  145. Nous emprunterons des idées à chacune des suggestions ci-dessus : nous
  146. utiliserons les chemins absolus, déterminé en utilisant le
  147. <code>realpath()</code>&#160;; nous positionnerons Zend Framework au plus tôt
  148. dans l'<code>include_path</code> ; nous avons déjà vérifié les chemins
  149. d'inclusions nécessaires&#160;; et nous mettrons le dossier courant comme
  150. dernier chemin. En fait, nous faisons tout bien ici - nous allons donc terminer
  151. avec seulement deux chemins.
  152. </para>
  153. <programlisting language="php"><![CDATA[
  154. $paths = array(
  155. realpath(dirname(__FILE__) . '/../library'),
  156. '.'
  157. );
  158. set_include_path(implode(PATH_SEPARATOR, $paths));
  159. ]]></programlisting>
  160. </example>
  161. </sect3>
  162. </sect2>
  163. <sect2 id="performance.classloading.striprequires">
  164. <title>Comment éliminer les déclarations require_once non nécessaires ?</title>
  165. <para>
  166. Le chargement tardif ("lazy loading") est une technique d'optimisation conçue pour
  167. repousser l'opération coûteuse de chargement d'une classe jusqu'au dernier moment
  168. possible - c'est-à-dire lors de l'instanciation d'un objet de cette classe, ou lors de
  169. l'utilisation d'une constante de classe ou d'une propriété statique. PHP supporte tout
  170. ceci via l'autoloading (ou "chargement automatique"), ce qui vous permet de définir un
  171. ou plusieurs callbacks à exécuter dans le but de faire correspondre un nom de classe à
  172. un fichier.
  173. </para>
  174. <para>
  175. Cependant, la plupart des avantages que vous pourrez retirer de l'autoloading sont
  176. diminués si le code de votre librairie exécute toujours des appels à
  177. <code>require_once</code> - ce qui est précisément le cas de Zend Framework. La question
  178. est donc : comment éliminer ces déclarations <code>require_once</code> dans le but de
  179. maximiser les performances de l'autoloader.
  180. </para>
  181. <sect3 id="performance.classloading.striprequires.sed">
  182. <title>Effacer les appels de require_once avec find et sed</title>
  183. <para>
  184. Une manière simple d'effacer les appels require_once est d'utiliser les
  185. utilitaires Unix "find" en conjonction avec "sed" pour passe en commentaires tous
  186. les appels. Essayez d'exécuter les commandes suivantes (où "%" indique le prompteur
  187. shell)&#160;:
  188. </para>
  189. <programlisting language="shell"><![CDATA[
  190. % cd chemin/vers/la/librarie/ZendFramework
  191. % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
  192. -not -wholename '*/Application.php' -print0 | \
  193. xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
  194. ]]></programlisting>
  195. <para>
  196. Cette ligne unique (coupée en deux pour la lisibilité) itère parmi les
  197. fichiers PHP et y remplace toute les instances de <code>require_once</code> par
  198. <code>//require_once</code>, c'est-à-dire en commentant toutes ces lignes (tout
  199. en maintenant les appels à <function>require_once</function> dans
  200. <classname>Zend_Application</classname> et
  201. <classname>Zend_Loader_Autoloader</classname>, puisque ces classes tomberont en
  202. erreur sans ceux-ci).
  203. </para>
  204. <para>
  205. Cette commande peut être simplement ajoutée à un script de construction
  206. automatique ou à un processus de mise en production, permettent ainsi d'augmenter
  207. les performances de votre application en production. Il est à noter, cependant, que
  208. si vous utilisez cette technique, vous <emphasis>devez</emphasis> utiliser
  209. l'autoloading ; vous pouvez l'activer dans votre fichier
  210. <filename>public/index.php</filename> en ajoutant le code suivant&#160;:
  211. </para>
  212. <programlisting language="php"><![CDATA[
  213. require_once 'Zend/Loader/Autoloader.php';
  214. Zend_Loader_Autoloader::getInstance();
  215. ]]></programlisting>
  216. </sect3>
  217. </sect2>
  218. <sect2 id="performance.classloading.pluginloader">
  219. <title>Comment accélérer le chargement des plugins&#160;?</title>
  220. <para>
  221. Certains composants utilisent les plugins, ce qui vous permet de créer vos propres
  222. classes afin de les utiliser avec le composant, de même que de surcharger les plugins
  223. standard existants embarqués dans Zend Framework. Ceci fournit une importante
  224. flexibilité au framework, mais a un prix&#160;: le chargement des plugins est une tâche
  225. assez coûteuse.
  226. </para>
  227. <para>
  228. Le chargeur de plugins vous permet de définir des paires préfixe de classe /
  229. chemin, vous autorisant ainsi à spécifier des fichiers de classe dans des chemins de
  230. dossiers non standard. Chaque préfixe peut avoir de multiples chemins associés. En
  231. interne, le chargeur de plugins boucle à travers chaque préfixe, et ensuite à travers
  232. chaque chemin lui étant associé, en testant l'existence du fichier et s'il est
  233. accessible dans ce chemin. Il le charge ensuite, et teste pour voir si la classe
  234. recherchée est bien disponible. Comme vous pouvez l'imaginer, tout ceci entraîne des
  235. appels aux stats du système de fichiers.
  236. </para>
  237. <para>
  238. Multipliez ceci par le nombre de composants qui utilisent le PluginLoader, et vous
  239. aurez une idée de l'importance de ce problème. Au moment de l'écriture de ce document,
  240. les composants suivants utilisent le PluginLoader&#160;:
  241. </para>
  242. <itemizedlist>
  243. <listitem>
  244. <para>
  245. <classname>Zend_Controller_Action_HelperBroker</classname>&#160;: aides
  246. d'action
  247. </para>
  248. </listitem>
  249. <listitem>
  250. <para>
  251. <classname>Zend_Dojo</classname>&#160;: aides de vues, éléments de formulaires
  252. et décorateurs
  253. </para>
  254. </listitem>
  255. <listitem>
  256. <para><classname>Zend_File_Transfer</classname>&#160;: adaptateurs</para>
  257. </listitem>
  258. <listitem>
  259. <para>
  260. <classname>Zend_Filter_Inflector</classname>&#160;: filtres (utilisé par l'aide
  261. d'action <code>ViewRenderer</code> et <classname>Zend_Layout</classname>)
  262. </para>
  263. </listitem>
  264. <listitem>
  265. <para><classname>Zend_Filter_Input</classname>&#160;: filtres et validateurs</para>
  266. </listitem>
  267. <listitem>
  268. <para>
  269. <classname>Zend_Form</classname>&#160;: éléments, validateurs, filtres,
  270. décorateurs, captcha et adaptateur pour les transferts de fichiers
  271. </para>
  272. </listitem>
  273. <listitem>
  274. <para><classname>Zend_Paginator</classname>&#160;: adaptateurs</para>
  275. </listitem>
  276. <listitem>
  277. <para><classname>Zend_View</classname>&#160;: aides de vues, filtres</para>
  278. </listitem>
  279. </itemizedlist>
  280. <para>Comment réduire le nombre des appels réalisés&#160;?</para>
  281. <sect3 id="performance.classloading.pluginloader.includefilecache">
  282. <title>Utiliser le fichier de cache des inclusions du PluginLoader</title>
  283. <para>
  284. Zend Framework 1.7.0 ajoute un fichier de cache des inclusions au
  285. PluginLoader. Cette fonctionnalité écrit dans un fichier les appels
  286. "<code>include_once</code>", que vous pouvez ensuite inclure dans votre fichier
  287. d'amorçage. Même si ceci introduit de nouveaux appels include_once dans votre code,
  288. cela permet de s'assurer que le PluginLoader les retournera au plus vite.
  289. </para>
  290. <para>
  291. La documentation du PluginLoader <link
  292. linkend="zend.loader.pluginloader.performance.example">inclue un exemple complet de
  293. son utilisation</link>.
  294. </para>
  295. </sect3>
  296. </sect2>
  297. </sect1>