| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- EN-Revision: 24249 -->
- <!-- Reviewed: no -->
- <sect1 id="performance.classloading">
- <title>Laden von Klassen</title>
- <para>
- Jeder der jemals Profiling von Zend Framework Anwendungen durchführen muß, wird sofort
- feststellen dass das Laden von Klassen relativ teuer ist im Zend Framework. Zwischen der
- reinen Anzahl von Klassendateien die für viele Komponenten geladen werden müssen, und der
- Verwendung von Plugins die keine 1:1 Verknüpfung zwischen Ihrem Klassennamen und dem
- Dateisystem haben, können die Aufrufe zu <methodname>include_once()</methodname> und
- <methodname>require_once()</methodname> problematisch sein. Diese Kapitel versucht einige
- konkrete Lösungen für diese Probleme zu geben.
- </para>
- <sect2 id="performance.classloading.includepath">
- <title>Wie kann ich meinen include_path optimieren?</title>
- <para>
- Eine triviale Optimierung die man machen kann um die Geschwindigkeit für das Laden der
- Klassen zu erhöhen ist es, auf den include_path besonders Rücksicht zu nehmen. Im
- speziellen, sollte man vier Dinge tun: Absolute Pfade verwenden (oder Pfade relativ zu
- absoluten Pfaden), die Anzahl der Include Pfade die man definiert reduzieren, den
- include_path von Zend Framework so früh wie möglich zu haben, und am Ende von
- include_path nur den aktuellen Verzechnispfad inkludieren.
- </para>
- <sect3 id="performance.classloading.includepath.abspath">
- <title>Absolute Pfade verwenden</title>
- <para>
- Auch wenn das wie eine Mikro-Optimierung aussieht, ist es Fakt das wenn man es
- nicht durchführt, die Vorteile von <acronym>PHP</acronym>'s RealPath Cache sehr
- klein sind, und als Ergebnis, das OpCode Caching nicht so schnell sein wird wie man
- erwarten könnte.
- </para>
- <para>
- Es gibt zwei einfache Wege um das Sicherzustellen. Erstens, kann man die Pfade in
- der <filename>php.ini</filename>, <filename>httpd.conf</filename>, oder
- <filename>.htaccess</filename> Hardcoded hineinschreiben. Zweitens, kann man
- <acronym>PHP</acronym>'s <methodname>realpath()</methodname> Funktion verwendet
- wenn man den include_path setzt:
- </para>
- <programlisting language="php"><![CDATA[
- $paths = array(
- realpath(dirname(__FILE__) . '/../library'),
- '.',
- );
- set_include_path(implode(PATH_SEPARATOR, $paths);
- ]]></programlisting>
- <para>
- Man <emphasis>kann</emphasis> relative Pfade verwenden -- solange Sie relativ zu
- einem absoluten Pfad sind:
- </para>
- <programlisting language="php"><![CDATA[
- define('APPLICATION_PATH', realpath(dirname(__FILE__)));
- $paths = array(
- APPLICATION_PATH . '/../library'),
- '.',
- );
- set_include_path(implode(PATH_SEPARATOR, $paths);
- ]]></programlisting>
- <para>
- Wie auch immer, selbst auf diese Art, ist es typischerweise eine triviale Aufgabe
- um den Pfad einfach an <methodname>realpath()</methodname> zu übergeben.
- </para>
- </sect3>
- <sect3 id="performance.classloading.includepath.reduce">
- <title>Die Anzahl der Include Pfade die man definiert reduzieren</title>
- <para>
- Include Pfade werden in der Reihenfolge in der Sie im include_path vorkommen
- durchsucht. Natürlich bedeutet das, das man ein Ergebnis schneller erhält wenn die
- Datei im ersten Scan gefunden wird, statt im letzten. Deswegen ist eine
- offensichtliche Verbesserung wenn man einfach die Anzahl der Pfade im include_path
- nur auf die reduziert die man benötigt. Man muß sich jeden include_path den man
- definiert hat anschauen, und feststellen ob aktuell irgendeine Funktionalität in
- diesem Pfad ist, die in der eigenen Anwendung verwendet wird; wenn nicht, dann
- entfernen Sie ihn.
- </para>
- <para>
- Eine weitere Optimierung ist es Pfade zu kombinieren. Zum Beispiel folgt Zend
- Framework der Namenskonvention von <acronym>PEAR</acronym>; deswegen kann man, wenn
- man <acronym>PEAR</acronym> Bibliotheken verwendet (oder Bibliotheken von anderen
- Frameworks oder Komponentenbibliotheken die dem <acronym>PEAR</acronym> CS folgen),
- versuchen alle diese Bibliotheken in den gleichen include_path zu geben. Das kann
- oft durchgeführt werden indem etwas einfaches wie ein SymLink auf eine oder mehrere
- Bibliotheken in ein generelles Verzeichnis gelegt wird.
- </para>
- </sect3>
- <sect3 id="performance.classloading.includepath.early">
- <title>Definiere den include_path zum Zend Framework so früh wie möglich</title>
- <para>
- Als Fortführung des vorherigen Vorschlags, ist eine weitere offensichtliche
- Optimierung die Definierung vom include_path vom Zend Framework so früh wie möglich
- im include_path. In den meisten Fällen sollte er der Erste in der Liste sein. Das
- stellt sicher das die Dateien die vom Zend Framework included werden schon beim
- Ersten Scan gefunden werden.
- </para>
- </sect3>
- <sect3 id="performance.classloading.includepath.currentdir">
- <title>Definiere das aktuelle Verzeichnis als letztes oder gar nicht</title>
- <para>
- Die meisten Beispiele von include_path zeigen die Verwendung des aktuellen
- Verzeichnisses oder '.'. Das ist üblich um sicherzustellen das Skripte die im
- gleichen Verzeichnis wie die Datei die Sie benötigt geladen werden können.
- Trotzdem, zeigen die gleichen Beispiele typischerweise dieses Pfadelement als
- erstes Element im include_path -- was bedeuetet das der aktuelle Verzeichnisbaum
- immer zuerst gescannt wird. In den meisten Fällen, wenn man Zend Framework
- Anwendungen hat, ist das nicht gewünscht, und der Pfad kann ohne Probleme als
- letztes Element in der Liste verschoben werden.
- </para>
- <example id="performance.classloading.includepath.example">
- <title>Beispiel: Optimierter include_path</title>
- <para>
- Fügen wir also alle diese Vorschläge zusammen. Unsere Annahme wird sein, das
- man ein oder mehrere <acronym>PEAR</acronym> Bibliotheken in Verbindung mit dem
- Zend Framework verwendet -- möglicherweise die PHPUnit und
- <classname>Archive_Tar</classname> Bibliotheken -- und das man
- offensichtlicherweise die Dateien relativ zur aktuellen Datei einfügen muß.
- </para>
- <para>
- Zuerst, erstellen wir ein Bibliotheksverzeichnis in unserem Projekt. Innerhalb
- dieses Verzeichnisses, erstellen wir einen Symlink zu unserer Zend Framework
- <filename>library/Zend</filename> Verzeichnis, wie auch dem notwendigen
- Verzeichnis von unserer <acronym>PEAR</acronym> Installation:
- </para>
- <programlisting language="php"><![CDATA[
- library
- Archive/
- PEAR/
- PHPUnit/
- Zend/
- ]]></programlisting>
- <para>
- Das erlaubt es und unseren eigenen Blbiliothekscode hinzuzufügen wenn das
- notwendig werden sollte, wärend andere verwendete Bibliotheken intakt bleiben.
- </para>
- <para>
- Als nächstes erstellen wir unseren include_path programmtechnisch in unserer
- <filename>public/index.php</filename> Datei. Das erlaubt es uns unseren Code in
- unserem Dateisystem zu verschieben, ohne das es notwendig ist jedesmal den
- include_path zu bearbeiten.
- </para>
- <para>
- Wir borgen uns Ideen von jedem der obigen Vorschläge aus: Wir verwenden
- absolute Pfade, die durch die Verwendung von
- <methodname>realpath()</methodname> erkannt werden; wir fügen den Zend
- Framework so früh wie möglich in den include_path ein; wir haben bereits
- Include Pfade erstellt; und wir geben das aktuelle Verzeichnis als letzten Pfad
- hinein. Faktisch, machen wir es hier sehr gut -- wir werden mit nur zwei Pfaden
- enden.
- </para>
- <programlisting language="php"><![CDATA[
- $paths = array(
- realpath(dirname(__FILE__) . '/../library'),
- '.'
- );
- set_include_path(implode(PATH_SEPARATOR, $paths));
- ]]></programlisting>
- </example>
- </sect3>
- </sect2>
- <sect2 id="performance.classloading.striprequires">
- <title>Wie kann man unnötige require_once Anweisungen entfernen?</title>
- <para>
- Lazy Loading ist eine Optimierungstechnik die entwickelt wurde um die teure Operation
- des Ladens einer Klassendatei bis zum Letztmöglichen Moment zu verzögern -- bzw., wenn
- ein Objekt dieser Klasse instanziiert wird, wenn eine statische Klassenmethode
- aufgerufen wird, oder wenn auf eine Klassenkonstante oder statische Eigenschaft
- referenziert wird. <acronym>PHP</acronym> unterstützt das durch Autoloading, welches es
- erlaubt ein oder mehrere Callbacks zu definieren die in Reihenfolge aufgerufen werden
- um einen Klassennamen mit einer Datei zu verbinden.
- </para>
- <para>
- Trotzdem sind die meisten Vorteile man Autoloading erwarten könnte, hinfällig wenn der
- Bibliothekscode weiterhin <methodname>require_once()</methodname> Aufrufe durchführt --
- was präzise der Fall ist beim Zend Framework. Die Frage ist also: Wie kann man diese
- <methodname>require_once()</methodname> Aufrufe entfernen um die Geschwindigkeit vom
- Autoloader zu maximieren?
- </para>
- <sect3 id="performance.classloading.striprequires.sed">
- <title>Aufrufe von require_once mit find und sed entfernen</title>
- <para>
- Ein einfacher Weg um <methodname>require_once()</methodname> Aufrufe zu entfernen
- ist die Verwendung der <acronym>UNIX</acronym> Utilities 'find' und 'set' in
- Verbindung um jeden Aufruf auszukommentieren. Führe die folgenden Anweisungen aus
- (wobei '%' der Shell Prompt ist):
- </para>
- <programlisting language="shell"><![CDATA[
- % cd path/to/ZendFramework/library
- % find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
- -not -wholename '*/Application.php' -print0 | \
- xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
- ]]></programlisting>
- <para>
- Dieser Ein-Zeiler (wegen der Lesbarkeit in zwei Zeilen gebrochen) geht durch jede
- <acronym>PHP</acronym> Datei und sagt Ihr das jede Instanz von 'require_once' mit
- '// require_once' ersetzt werden soll, was jede dieser Anweisungen effektiv
- auskommentiert. (Es stellt sicher das <methodname>require_once()</methodname>
- Aufrufe innerhalb von <classname>Zend_Application</classname> und
- <classname>Zend_Loader_Autoloader</classname> bleiben, da diese Klassen ohne Sie
- nicht funktionieren.)
- </para>
- <para>
- Dieses Kommando sollte in einem automatischen Build oder Release Prozess ganz
- trivial hinzugefügt werden. Es sollte trotzdem klar sein das man, wenn man diese
- Technik verwendet, Autoloading verwendetn <emphasis>muss</emphasis>; man kann das
- von der eigenen "<filename>public/index.php</filename>" Datei mit dem folgenden
- Code tun:
- </para>
- <programlisting language="php"><![CDATA[
- require_once 'Zend/Loader/Autoloader.php';
- Zend_Loader_Autoloader::getInstance();
- ]]></programlisting>
- </sect3>
- </sect2>
- <sect2 id="performance.classloading.pluginloader">
- <title>Wie kann ich das Laden der Plugins beschleunigen?</title>
- <para>
- Viele Komponenten haben Plugins, welche es erlauben eigene Klassen zu Erstellen und in
- der Komponente zu verwenden, sowie bestehende Standardplugins vom Zend Framework, zu
- überladen. Das bietet eine wichtige Flexibilität für den Framework, aber zu einem
- Preis: Das Laden der Plugins ist eine recht teure Aufgabe.
- </para>
- <para>
- Der Pluginlader erlaubt es Klassenpräfixe / Pfadpaare zu registrieren, was es erlaubt
- Klassendateien in nicht-standard Pfaden zu spezifizieren. Jeder Präfix kann mehrere mit
- Ihm assoziierte Pfade haben. Intern durchläuft der Pluginlader jeden Präfix, und dann
- jeden Ihm angehängten Pfad, testet od die Datei existiert und unter diesem Pfad lesbar
- ist. Dann lädt er Sie, und testet ob die Klasse nach der er sucht vorhanden ist. Sie
- man sich vorstellen kann, kann das zu vielen Aufrufe auf das Dateisystem führen.
- </para>
- <para>
- Multipliziert mit der anzahl der Komponenten die den PluginLoader verwenden, und man
- bekommt eine Idee von der Reichweite des Problems. Zu der Zeit zu der das geschrieben
- wird, verwenden die folgenden Komponenten den PluginLoader:
- </para>
- <itemizedlist>
- <listitem>
- <para><classname>Zend_Controller_Action_HelperBroker</classname>: Helfer</para>
- </listitem>
- <listitem>
- <para>
- <classname>Zend_Dojo</classname>: View Helfer, Form Elemente und Dekorator
- </para>
- </listitem>
- <listitem>
- <para><classname>Zend_File_Transfer</classname>: Adapter</para>
- </listitem>
- <listitem>
- <para>
- <classname>Zend_Filter_Inflector</classname>: Filter (verwendet vom ViewRenderer
- Action Helfer und <classname>Zend_Layout</classname>)
- </para>
- </listitem>
- <listitem>
- <para><classname>Zend_Filter_Input</classname>: Filter und Prüfungen</para>
- </listitem>
- <listitem>
- <para>
- <classname>Zend_Form</classname>: Elemente, Prüfungen, Filter, Dekoratore,
- Captcha und File Transfer Adapter
- </para>
- </listitem>
- <listitem><para><classname>Zend_Paginator</classname>: Adapter</para></listitem>
- <listitem><para><classname>Zend_View</classname>: Helfer, Filter</para></listitem>
- </itemizedlist>
- <para>
- Wie kann man die Anzahl der so gemachten Aufrufe reduzieren?
- </para>
- <sect3
- id="performance.classloading.pluginloader.includefilecache">
- <title>Verwenden des PluginLoaders Include-File Caches</title>
- <para>
- Zend Framework 1.7.0 fügt einen Include-File Cache zum PluginLoader hinzu. Diese
- Funktionalität schreibt "<methodname>include_once()</methodname>" Aufrufe in eine
- Datei, welche man dann in der Bootstrap Datei einfügen (include) kann. Wärend das
- einen extra <methodname>include_once()</methodname> Aufruf im Code bedeutet, stellt
- es auch sicher das der PluginLoader so früh wie möglich zurückkehrt.
- </para>
- <para>
- Die PluginLoader Dokumentation
- <link linkend="zend.loader.pluginloader.performance.example"> enthält ein kompettes
- Beispiel seiner Verwendung</link>.
- </para>
- </sect3>
- </sect2>
- </sect1>
|