performance-classloading.xml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 15617 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="performance.classloading">
  5. <title>Laden von Klassen</title>
  6. <para>
  7. Jeder der jemals Profiling von Zend Framework Anwendungen durchführen muß, wird sofort
  8. feststellen das das Laden von Klassen relativ teuer ist im Zend Framework. Zwischen der
  9. reinen Anzahl von Klassendateien die für viele Komponenten geladen werden müssen, und der
  10. Verwendung von Plugins die keine 1:1 Verknüpfung zwischen Ihrem Klassennamen und dem
  11. Dateisystem haben, können die Aufrufe zu <methodname>include_once</methodname> und
  12. <methodname>require_once</methodname> problematisch sein. Diese Kapitel versucht einige
  13. konkrete Lösungen für diese Probleme zu geben.
  14. </para>
  15. <sect2 id="performance.classloading.includepath">
  16. <title>Wie kann ich meinen include_path optimieren?</title>
  17. <para>
  18. Eine triviale Optimierung die man machen kann um die Geschwindigkeit für das Laden der
  19. Klassen zu erhöhen ist es, auf den include_path besonders Rücksicht zu nehmen. Im
  20. speziellen, sollte man vier Dinge tun: Absolute Pfade verwenden (oder Pfade relativ zu
  21. absoluten Pfaden), die Anzahl der Include Pfade die man definiert reduzieren, den
  22. include_path von Zend Framework so früh wie möglich zu haben, und am Ende von
  23. include_path nur den aktuellen Verzechnispfad inkludieren.
  24. </para>
  25. <sect3 id="performance.classloading.includepath.abspath">
  26. <title>Absolute Pfade verwenden</title>
  27. <para>
  28. Auch wenn das wie eine Mikro-Optimierung aussieht, ist es Fakt das wenn man es
  29. nicht durchführt, die Vorteile von PHP's RealPath Cache sehr klein sind, und als
  30. Ergebnis, das OpCode Caching nicht so schnell sein wird wie man erwarten könnte.
  31. </para>
  32. <para>
  33. Es gibt zwei einfache Wege um das Sicherzustellen. Erstens, kann man die Pfade in
  34. der php.ini, httpd.conf, oder .htaccess Hardcoded hineinschreiben. Zweitens, kann
  35. man PHP's <methodname>realpath()</methodname> Funktion verwendet wenn man den
  36. include_path setzt:
  37. </para>
  38. <programlisting language="php"><![CDATA[
  39. $paths = array(
  40. realpath(dirname(__FILE__) . '/../library'),
  41. '.',
  42. );
  43. set_include_path(implode(PATH_SEPARATOR, $paths);
  44. ]]></programlisting>
  45. <para>
  46. Man <emphasis>kann</emphasis> relative Pfade verwenden -- solange Sie relativ zu
  47. einem absoluten Pfad sind:
  48. </para>
  49. <programlisting language="php"><![CDATA[
  50. define('APPLICATION_PATH', realpath(dirname(__FILE__)));
  51. $paths = array(
  52. APPLICATION_PATH . '/../library'),
  53. '.',
  54. );
  55. set_include_path(implode(PATH_SEPARATOR, $paths);
  56. ]]></programlisting>
  57. <para>
  58. Wie auch immer, selbst auf diese Art, ist es typischerweise eine triviale Aufgabe
  59. um den Pfad einfach an <methodname>realpath()</methodname> zu übergeben.
  60. </para>
  61. </sect3>
  62. <sect3 id="performance.classloading.includepath.reduce">
  63. <title>Die Anzahl der Include Pfade die man definiert reduzieren</title>
  64. <para>
  65. Include Pfade werden in der Reihenfolge in der Sie im include_path vorkommen
  66. durchsucht. Natürlich bedeutet das, das man ein Ergebnis schneller erhält wenn die
  67. Datei im ersten Scan gefunden wird, statt im letzten. Deswegen ist eine
  68. offensichtliche Verbesserung wenn man einfach die Anzahl der Pfade im include_path
  69. nur auf die reduziert die man benötigt. Man muß sich jeden include_path den man
  70. definiert hat anschauen, und feststellen ob aktuell irgendeine Funktionalität in
  71. diesem Pfad ist, die in der eigenen Anwendung verwendet wird; wenn nicht, dann
  72. entfernen Sie ihn.
  73. </para>
  74. <para>
  75. Eine weitere Optimierung ist es Pfade zu kombinieren. Zum Beispiel folgt Zend
  76. Framework der Namenskonvention von PEAR; deswegen kann man, wenn man PEAR
  77. Bibliotheken verwendet (oder Bibliotheken von anderen Frameworks oder
  78. Komponentenbibliotheken die dem PEAR CS folgen), versuchen alle diese Bibliotheken
  79. in den gleichen include_path zu geben. Das kann oft durchgeführt werden indem etwas
  80. einfaches wie ein SymLink auf eine oder mehrere Bibliotheken in ein generelles
  81. Verzeichnis gelegt wird.
  82. </para>
  83. </sect3>
  84. <sect3 id="performance.classloading.includepath.early">
  85. <title>Definiere den include_path zum Zend Framework so früh wie möglich</title>
  86. <para>
  87. Als Fortführung des vorherigen Vorschlags, ist eine weitere offensichtliche
  88. Optimierung die Definierung vom include_path vom Zend Framework so früh wie möglich
  89. im include_path. In den meisten Fällen sollte er der Erste in der Liste sein. Das
  90. stellt sicher das die Dateien die vom Zend Framework included werden schon beim
  91. Ersten Scan gefunden werden.
  92. </para>
  93. </sect3>
  94. <sect3 id="performance.classloading.includepath.currentdir">
  95. <title>Definiere das aktuelle Verzeichnis als letztes oder gar nicht</title>
  96. <para>
  97. Die meisten Beispiele von include_path zeigen die Verwendung des aktuellen
  98. Verzeichnisses oder '.'. Das ist üblich um sicherzustellen das Skripte die im
  99. gleichen Verzeichnis wie die Datei die Sie benötigt geladen werden können.
  100. Trotzdem, zeigen die gleichen Beispiele typischerweise dieses Pfadelement als
  101. erstes Element im include_path -- was bedeuetet das der aktuelle Verzeichnisbaum
  102. immer zuerst gescannt wird. In den meisten Fällen, wenn man Zend Framework
  103. Anwendungen hat, ist das nicht gewünscht, und der Pfad kann ohne Probleme als
  104. letztes Element in der Liste verschoben werden.
  105. </para>
  106. <example id="performance.classloading.includepath.example">
  107. <title>Beispiel: Optimierter include_path</title>
  108. <para>
  109. Fügen wir also alle diese Vorschläge zusammen. Unsere Annahme wird sein, das
  110. man ein oder mehrere PEAR Bibliotheken in Verbindung mit dem Zend Framework
  111. verwendet -- möglicherweise die PHPUnit und Archive_Tar Bibliotheken -- und
  112. das man offensichtlicherweise die Dateien relativ zur aktuellen Datei einfügen
  113. muß.
  114. </para>
  115. <para>
  116. Zuerst, erstellen wir ein Bibliotheksverzeichnis in unserem Projekt. Innerhalb
  117. dieses Verzeichnisses, erstellen wir einen Symlink zu unserer Zend Framework
  118. <filename>library/Zend</filename> Verzeichnis, wie auch dem notwendigen
  119. Verzeichnis von unserer PEAR Installation:
  120. </para>
  121. <programlisting language="php"><![CDATA[
  122. library
  123. Archive/
  124. PEAR/
  125. PHPUnit/
  126. Zend/
  127. ]]></programlisting>
  128. <para>
  129. Das erlaubt es und unseren eigenen Blbiliothekscode hinzuzufügen wenn das
  130. notwendig werden sollte, wärend andere verwendete Bibliotheken intakt bleiben.
  131. </para>
  132. <para>
  133. Als nächstes erstellen wir unseren include_path programmtechnisch in unserer
  134. <filename>public/index.php</filename> Datei. Das erlaubt es uns unseren Code in
  135. unserem Dateisystem zu verschieben, ohne das es notwendig ist jedesmal den
  136. include_path zu bearbeiten.
  137. </para>
  138. <para>
  139. Wir borgen uns Ideen von jedem der obigen Vorschläge aus: Wir verwenden
  140. absolute Pfade, die durch die Verwendung von
  141. <methodname>realpath()</methodname> erkannt werden; wir fügen den Zend
  142. Framework so früh wie möglich in den include_path ein; wir haben bereits
  143. Include Pfade erstellt; und wir geben das aktuelle Verzeichnis als letzten Pfad
  144. hinein. Faktisch, machen wir es hier sehr gut -- wir werden mit nur zwei Pfaden
  145. enden.
  146. </para>
  147. <programlisting language="php"><![CDATA[
  148. $paths = array(
  149. realpath(dirname(__FILE__) . '/../library'),
  150. '.'
  151. );
  152. set_include_path(implode(PATH_SEPARATOR, $paths));
  153. ]]></programlisting>
  154. </example>
  155. </sect3>
  156. </sect2>
  157. <sect2 id="performance.classloading.striprequires">
  158. <title>Wie kann man unnötige require_once Anweisungen entfernen?</title>
  159. <para>
  160. Lazy Loading ist eine Optimierungstechnik die entwickelt wurde um die teure Operation
  161. des Ladens einer Klassendatei bis zum Letztmöglichen Moment zu verzögern -- bzw., wenn
  162. ein Objekt dieser Klasse instanziiert wird, wenn eine statische Klassenmethode
  163. aufgerufen wird, oder wenn auf eine Klassenkonstante oder statische Eigenschaft
  164. referenziert wird. PHP unterstützt das durch Autoloading, welches es erlaubt ein oder
  165. mehrere Callbacks zu definieren die in Reihenfolge aufgerufen werden um einen
  166. Klassennamen mit einer Datei zu verbinden.
  167. </para>
  168. <para>
  169. Trotzdem sind die meisten Vorteile man Autoloading erwarten könnte, hinfällig wenn der
  170. Bibliothekscode weiterhin require_once Aufrufe durchführt -- was präzise der Fall ist
  171. beim Zend Framework. Die Frage ist also: Wie kann man diese require_once Aufrufe
  172. entfernen um die Geschwindigkeit vom Autoloader zu maximieren?
  173. </para>
  174. <sect3 id="performance.classloading.striprequires.sed">
  175. <title>Aufrufe von require_once mit find und sed entfernen</title>
  176. <para>
  177. Ein einfacher Weg um require_once Aufrufe zu entfernen ist die Verwendung der UNIX
  178. Utilities 'find' und 'set' in Verbindung um jeden Aufruf auszukommentieren. Führe
  179. die folgenden Anweisungen aus (wobei '%' der Shell Prompt ist):
  180. </para>
  181. <programlisting language="shell"><![CDATA[
  182. % cd path/to/ZendFramework/library
  183. % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' -print0 | \
  184. xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
  185. ]]></programlisting>
  186. <para>
  187. Dieser Ein-Zeiler (wegen der Lesbarkeit in zwei Zeilen gebrochen) geht durch jede
  188. PHP Datei und sagt Ihr das jede Instanz von 'require_once' mit '// require_once'
  189. ersetzt werden soll, was jede dieser Anweisungen effektiv auskommentiert.
  190. </para>
  191. <para>
  192. Dieses Kommando sollte in einem automatischen Build oder Release Prozess ganz
  193. trivial hinzugefügt werden. Es sollte trotzdem klar sein das man, wenn man diese
  194. Technik verwendet, Autoloading verwendetn <emphasis>muss</emphasis>; man kann das
  195. von der eigenen "<filename>public/index.php</filename>" Datei mit dem folgenden
  196. Code tun:
  197. </para>
  198. <programlisting language="php"><![CDATA[
  199. require_once 'Zend/Loader/Autoloader.php';
  200. Zend_Loader_Autoloader::getInstance();
  201. ]]></programlisting>
  202. </sect3>
  203. </sect2>
  204. <sect2 id="performance.classloading.pluginloader">
  205. <title>Wie kann ich das Laden der Plugins beschleunigen?</title>
  206. <para>
  207. Viele Komponenten haben Plugins, welche es erlauben eigene Klassen zu Erstellen und in
  208. der Komponente zu verwenden, sowie bestehende Standardplugins vom Zend Framework, zu
  209. überladen. Das bietet eine wichtige Flexibilität für den Framework, aber zu einem
  210. Preis: Das Laden der Plugins ist eine recht teure Aufgabe.
  211. </para>
  212. <para>
  213. Der Pluginlader erlaubt es Klassenpräfixe / Pfadpaare zu registrieren, was es erlaubt
  214. Klassendateien in nicht-standard Pfaden zu spezifizieren. Jeder Präfix kann mehrere mit
  215. Ihm assoziierte Pfade haben. Intern durchläuft der Pluginlader jeden Präfix, und dann
  216. jeden Ihm angehängten Pfad, testet od die Datei existiert und unter diesem Pfad lesbar
  217. ist. Dann lädt er Sie, und testet ob die Klasse nach der er sucht vorhanden ist. Sie
  218. man sich vorstellen kann, kann das zu vielen Aufrufe auf das Dateisystem führen.
  219. </para>
  220. <para>
  221. Multipliziert mit der anzahl der Komponenten die den PluginLoader verwenden, und man
  222. bekommt eine Idee von der Reichweite des Problems. Zu der Zeit zu der das geschrieben
  223. wird, verwenden die folgenden Komponenten den PluginLoader:
  224. </para>
  225. <itemizedlist>
  226. <listitem><para>
  227. <classname>Zend_Controller_Action_HelperBroker</classname>: Helfer
  228. </para></listitem>
  229. <listitem><para>
  230. <classname>Zend_Dojo</classname>: View Helfer, Form Elemente und Dekorator
  231. </para></listitem>
  232. <listitem><para>
  233. <classname>Zend_File_Transfer</classname>: Adapter
  234. </para></listitem>
  235. <listitem><para>
  236. <classname>Zend_Filter_Inflector</classname>: Filter (verwendet vom ViewRenderer
  237. Action Helfer und <classname>Zend_Layout</classname>)
  238. </para></listitem>
  239. <listitem><para>
  240. <classname>Zend_Filter_Input</classname>: Filter und Prüfungen
  241. </para></listitem>
  242. <listitem><para>
  243. <classname>Zend_Form</classname>: Elemente, Prüfungen, Filter, Dekoratore, Captcha
  244. und File Transfer Adapter
  245. </para></listitem>
  246. <listitem><para>
  247. <classname>Zend_Paginator</classname>: Adapter
  248. </para></listitem>
  249. <listitem><para>
  250. <classname>Zend_View</classname>: Helfer, Filter
  251. </para></listitem>
  252. </itemizedlist>
  253. <para>
  254. Wie kann man die Anzahl der so gemachten Aufrufe reduzieren?
  255. </para>
  256. <sect3
  257. id="performance.classloading.pluginloader.includefilecache">
  258. <title>Verwenden des PluginLoaders Include-File Caches</title>
  259. <para>
  260. Zend Framework 1.7.0 fügt einen Include-File Cache zum PluginLoader hinzu. Diese
  261. Funktionalität schreibt "include_once" Aufrufe in eine Datei, welche man dann in
  262. der Bootstrap Datei einfügen (include) kann. Wärend das einen extra include_once
  263. Aufruf im Code bedeutet, stellt es auch sicher das der PluginLoader so früh wie
  264. möglich zurückkehrt.
  265. </para>
  266. <para>
  267. Die PluginLoader Dokumentation
  268. <link linkend="zend.loader.pluginloader.performance.example"> enthält ein kompettes
  269. Beispiel seiner Verwendung</link>.
  270. </para>
  271. </sect3>
  272. </sect2>
  273. </sect1>
  274. <!--
  275. vim:se ts=4 sw=4 et:
  276. -->