PHP optimieren
Bei Jan Piotrowski habe ich heute morgen eine sehr umfassende Linksammlung für das Optimieren von PHP Anwendungen gefunden. Die Liste enthält zwar "nur" 8 Links, diese haben es aber teilweise in sich, will heißen, sie sind teilweise ziemlich umfangreich. Da brauche ich sicher ein paar Stunden, um mich dort einmal durchzuarbeiten.
Zwei weitere Tipps fallen mir auch noch auf die Schnelle ein (auch wenn ich nicht weiß, ob sie nicht vielleicht schon in den Links behandelt werden.
1. Array-Daten serialisiert laden
Vor vielen Monden habe ich einmal ein Benchmark für das Laden von größeren Arrays aus PHP Dateien durchgeführt. Neben einem einfachen include() bzw. require() habe ich auch getestet, wie schnell serialisierte Daten geladen und wieder deserialisiert werden. Dabei habe ich heraus gefunden, dass das Laden eines serialisierten Arrays aus einer Textdatei deutlich schneller vonstatten geht, als das Laden eines normalen Arrays aus einer PHP Datei.
Beispiel für ein nicht serialisiertes Array:
-
<?php
-
$_dobDef["main"]["dobFile" ] = "dob/user/main_edit.php";
-
$_dobDef["main"]["dobName" ] = "dobUserMainEdit";
-
$_dobDef["main"]["dobPkey" ] = "um_id";
-
$_dobDef["main"]["dobDataFunc"] = "updateActivation";
-
$_dobDef["main"]["transArea" ] = "dob_user_main";
-
$_dobDef["main"]["caching" ] = false;
-
?>
Nach der Serialisierung sehen die Daten dann so aus:
-
a:1:{s:4:"main";a:6:{s:7:"dobFile";s:22:"dob/user/main_edit.php";s:7:"dobName";s:15:"dobUserMainEdit";s:7:"dobPkey";s:5:"um_id";s:11:"dobDataFunc";s:16:"updateActivation";s:9:"transArea";s:13:"dob_user_main";s:7:"caching";b:0;}}
Das Laden dieser serialisierten Daten funktioniert fast so einfach wie ein include() bzw. require():
Das einzige Problem ist, dass man diese Datenfiles nach jeder Änderung erst einmal wieder serialisieren muss. Ich habe mir dafür ein kleines Skript geschrieben, welches ich dann nach dem Ändern der Daten einmal ausführen muss. Ich denke, dieser extra Verwaltungsaufwand ist die Performancesteigerung durchaus wert:
-
<?php
-
/**
-
* Convert definition files.
-
*
-
* This script helps to convert the table, tree and form definition files.
-
* The files contain serialized data arrays, which can be loaded and
-
* unserialized for usage in scripts. Loading and unserialization is much
-
* quicker than the inclusion of an array declaration in a php script file.
-
*
-
* @author Ralf Eggert <ralf@travello.de>
-
* @copyright Copyright 2003 - 2004, Ralf Eggert
-
* @version 0.3
-
* @package Data definition files
-
*/
-
-
/**
-
* Load configuration file
-
*/
-
require_once("cfg/admin.php");
-
-
/**
-
* Set some variables for
-
*/
-
-
"tree" => "_treeDef",
-
"relation" => "_relDef",
-
"form" => "_formDef",
-
"list" => "_listDef",
-
"view" => "_viewDef",
-
"dob" => "_dobDef",
-
"misc" => "_miscDef",
-
"lang" => "_langDef",
-
"filter" => "_filterDef",
-
"keylist" => "_keylistDef",
-
"mail" => "_mailDef");
-
-
$locDestDir = "def/files/";
-
$locSrcDir = "def/source/";
-
-
/**
-
* Fetch list of all files in definition directory
-
*/
-
-
-
foreach ($locDefTypeList as $locKey => $locVal)
-
{
-
-
{
-
{
-
continue;
-
}
-
elseif (".svn" == $locSrcFile)
-
{
-
continue;
-
}
-
-
$locSrcFileList[] = $locKey . "/" . $locSrcFile;
-
}
-
-
}
-
-
-
/**
-
* Clear all def type variables
-
*/
-
foreach ($locDefTypeList as $locDefType)
-
{
-
}
-
-
/**
-
* Process Source File list
-
*/
-
foreach ($locSrcFileList as $locSrcFile)
-
{
-
/**
-
* Get Definition Type
-
*/
-
$locDefFileDir = $locDefType[0];
-
$locDefType = $locDefTypeList[$locDefFileDir];
-
-
{
-
include($locSrcDir . $locSrcFile);
-
-
{
-
$locDestFile = $locDestDir . $locSrcFile;
-
-
/**
-
* Open file for writing
-
*/
-
$locFile = $_config["path"]["phppath"] . $locDestFile;
-
-
/**
-
* Serialize data array
-
*/
-
-
/**
-
* Wrote data and close file
-
*/
-
-
/**
-
* Unset data array
-
*/
-
-
echo "Conversion of definition file \"{$locSrcFile}\" <B>ok</B>!<br />\n";
-
}
-
else
-
{
-
echo "Conversion of definition file \"{$locSrcFile}\" <B>failed</B>!<br />\n";
-
}
-
}
-
}
-
?>
Ich habe das Skript jetzt nicht extra überarbeitet und optimiert. Es tut bereits seit Jahren seine Dienste. Einziger Hinweis: in $_config["path"]["phppath"] steht der Pfad zu allen PHP Dateien. Darunter befinden sich dann das Quellverzeichnis "def/source/" und das Verzeichnis mit den serialisierten Daten "def/files/".
Letzter Hinweis: die Benchmarks hierfür habe ich vor einigen Jahren unter PHP4 durchgeführt. Ob sich an diesem Verhalten mittlerweile etwas geändert hat, müsste ich noch einmal im Detail nachprüfen.
2. xdebug für das Profiling nutzen
Ein anderer Tipp stammt aus dem Buch "Enterprise PHP Tools", das ich vor einigen Tagen vorgestellt habe. Mit Hilfe der Profiling Funktionen von xdebug kann man detailliert überprüfen, welche Programmteile welche Laufzeit beanspruchen. So kann man schnell die Langläufer ausfindig machen und dort mit der Optimierung ansetzen.
Die etwas schwer zu lesenden Profiling Dateien, die xdebug erstellt, kann man mit Hilfe der Tools KCacheGrind (für Linux) und WinCacheGrind für Windows visualisieren.
Ich habe das selber noch nicht im Detail ausprobiert. Das steht aber ganz oben auf meinem Zettel. xdebug bietet zusätzlich zum Profiling noch viele andere nützliche Funktionen, die einem das Optimieren von PHP Code vereinfachen.


Montag, 28.08.2006, um 12:06
Xdebug2 ist wirklich absolut zu empfehlen. Wenn man nicht gerade komplett auf Funktionen und Includes verzichtet liefert es eine Menge wertvoller Hinweise. Aber nicht vergessen nach dem Profiling das Modul wieder zu deaktivieren, es kostet doch einiges an Zeit bei der Scriptsausführung.
Dienstag, 29.08.2006, um 10:40
Hi Jan,
ja, das ist klar, wenn man dauernd alle Optionen von xdebug aktiviert, dann macht das Entwickeln kaum noch Spass. Man hat dann aber viele Daten, die man auswerten könnte, wenn man mal Langeweile hat... ;-)
Gruß,
Ralf
Dienstag, 29.08.2006, um 12:51
Hallo Ralf,
ich verstehe gerade nicht, warum du beim unserialize einen implode machst.
Dienstag, 29.08.2006, um 13:05
Hallo Sebastian,
unserialize(implode("", file($inFile)));
Das file($inFile) läft die gesamte Datei in ein Array, wobei für jede Textzeile ein Eintrag im Array vorhanden ist. Weil unserialize() aber einen String benötigt, füge ich die Arrayelemente per implode() wieder zu einem String zusammen.
Gruß,
Ralf
Dienstag, 29.08.2006, um 13:17
Witzig, ich habe file gelesen und file_get_contents gedacht. Ich glaub ich sollte einen Kaffe trinken. :)
Dienstag, 29.08.2006, um 13:21
Stimmt, file_get_contents() würde an der Stelle viel mehr Sinn machen. Wie gesagt, habe ich das vor einigen Jahren mal so eingerichtet und seitdem nicht mehr angefasst, äh optimiert ;-)...
Gruß,
Ralf
Donnerstag, 12.10.2006, um 23:24
Bei meinen Tests in PHP 5.1.4 unter Windows kam ich immer zu dem eindeutigen Ergebnis, dass die Verwendung von serialisierten Arrays wesentlich langsamer ist, als die konventionelle Schreibweise. Etwas schneller als die übliche Variante ist die Schreibweise, die var_export() erzeugt. unserialize() braucht etwa dreimal soviel Zeit. Dateioperationen hab ich dabei allerdings nicht benutzt, sondern habe alles in eine einzige Datei geschrieben. Vielleicht ist der Flaschenhals eher bei include() (vs. file_get_contents()) zu suchen.
Generell denke ich, man sollte sich bei der Optimierung nicht zu sehr zum Sklaven der Technik machen lassen (CPU-Zeit dürfte auch günstiger sein als menschliche Arbeitszeit), zumal manche Geschwindigkeitsvorteile auch in zukünftigen Versionen hinfällig sein könnten. Solange es nicht um besonders zeitkritische Anwendungen geht, halte ich sauberen Code für wichtiger.