Scripting

ID #1239 PHP-Performance-Tipps

Problem:

PHP ist zwar eine einfache und schnell zu erlehrnende Sprache. Aber sobald man größere Projekte angeht, hat man schnell das Problem das die Geschwindigkeit sinkt.
Hier will ich einige Vorschläge zur Beschleunigung der Compiletime (also das übersetzen vom PHP-Script in Binary-Code) und der Runtime (also optimiertere Laufzeit) machen:

Die Tipps im Einzelnen:

1. PHP-Code innerhalb von HTML

In PHP sieht man häufig solche Konstrukte:

<a href="<?php echo $_SERVER['PHP_SELF'] ?>"?command=action>Action</a>

Ein Verschachteln von HTML und PHP hat zwei wesentliche Nachteile:
1.) Man verliert deutlich die Übersicht.
2.) Die Zend-Engine braucht mehr Zeit zum Parsen.

Der deutlich bessere Weg ist PHP-Scripte wirklich wie Scripte zu behandeln und keine Mischung aus PHP-&HTML-Code zu zu lassen.
D.h. Ein PHP-Script beginnt in der ersten Zeile mit <?php und endet in der letzten Zeile mit ?>.
Alle HTML-Ausgaben erfolgen mit echo, print oder printf.

2. Strings

Strings sind beliebige Zeichenketten. Und doch gibt es wesentliche Unterschiede in der Notation. Man kann in PHP String mit einfachen (') oder doppelten Hochkomma (") schreiben.
Der Unterschied ist den meisten PHP-Programmierer sogar geläufig:
- Ein 'string\n' wird genau so übernommen.
- Ein "string\n" hingegen erhält einen Zeilenumbruch.
- Ein "string=$str" löst sogar die Variable auf.

Aber warum nutzt man dann immer wieder "string" in doppelten Hochkommata, wenn man die Features des Parsings gar nicht nutzt?
Wenn man bedenkt, daß jeder String in " nochmals durch einen Parser läuft, so sollte man in Zukunft nur noch ' einsetzen und den doppelten lediglich wenn man Zeilenumbrüche braucht.

3. Laufzeit

Zur Optimierung der Laufzeit möchte ich folgende zwei Beispiele untersuchen:

for ($j=0; $j<sizeof($arr); $j++)
echo $arr[$j]."<br />";

for ($j=0, $max = sizeof($arr), $s = ''; $j<$max; $j++)
$s .= $arr[$j].'<br />';
echo $s;

Grundsätzlich scheint das 2te Beispiel deutlich länger und vor allem umständlicher zu sein. Zuerst vergleichen wir den Schleifen-Konstrukt:
Im Ersten wird sizeof($arr) bei jedem Schleifendurchlauf aufgerufen. Dies macht i.d.R. nur Sinn, wenn sich das Array innerhalb der Schleife ändern könnte. Ansonsten ist es schneller die Größe des Arrays nur einmal zu bestimmen und dann eine feste Schleife zu fahren wie es im Zweiten der Fall ist.
Dies gilt natürlich für alle Schleifen-Arten und Funktionen. Ebenfalls für den Durchlauf eines Datenbank-Recordset's und ähnlichem.

Nun noch ein Wort zu echo: Jede Funktion die HTML-Code an den Apache und den Browser zurück liefern soll ist aufwendiger und kostet Zeit. Denn bei jeder Ausgabe wird ein Aufruf in die Apache-API gestartet und evtl. TCP/IP-Pakete gebaut, etc.
Schneller geht es, wenn man die Ausgabe eine Weile in einer Variable zwischen lagert und dann in einem Rutsch an den Apache weiterleitet.

Zu guter Letzt noch ein Blick auf die verschiedenen Schleifen-Konstrukte:
Wir Vergleichen eine for-Schleife mit while:

for ($i=0; $i<$max; $i++) {
    ...
}

$i = 0;
while ($i++<$max) {
    ...
}

Beide Schleifen liefern das selbe Ergebnis. Erstaunlicherweise ist die while-Schleife aber deutlich schneller.
(Eine foreach-Schleife will ich erst gar nicht in den Vergleich aufnehmen, da jedem bereits klar sein dürfte, daß dies die unperformanteste Art ist ein Array zu durchlaufen.)

4. Modularisierung

Modularisierung ist ein sehr guten Paradigma der modernen Softwareentwicklung. Aber bei Interpreter-Sprachen wie z.B. PHP benötigt jede Einbindung von weiteren Dateien (z.B. mit include(), require(), require_once()) Zugriff auf die Platte und damit Compiler-Zeit.
Ein include() ist sowieso etwas besonderes, da er erst zur Laufzeit ausgeführt wird. D.h. während des Script-Ablaufes kann/wird die Datei erst mit in das Script eingefügt. Es wird also nachträglich nochmals der Parser (Zend-Engine) angeschmissen um diese Datei zu verarbeiten. Eine absolut Zeit intensive Aktion.

Beschleunigung messen

Um die eigenen Optimierungen zu messen kann man einfach mit der Funktion getmicrotime() arbeiten:

<?php
$start_time = getmicrotime();

// code
// ...

print (getmicrotime()-$start_time);
?>

Weiterführende Links:

 

sozial Bookmarking
Bookmarken bei YIGG Bookmarken bei Mister-Wong Bookmarken bei Icio Bookmarken bei del.icio.us Bookmarken bei Technorati Bookmarken bei Furl Bookmarken bei Spurl Bookmarken bei Yahoo Bookmarken bei Google

vom 2007-06-02 10:38, zuletzt 2007-06-02 10:50     Artikel ausdrucken Artikel weiterempfehlen Als PDF-Datei anzeigen

Dieser Inhalt ist unter der Creative-Commons Lizenz lizensiert.

Probleme bitte im Server-Support-Forum diskutieren.

überflüssig 1 2 3 4 5 wertvoll  
Durchschnittliche Bewertung:   4.8 von 5 (5 Bewertungen)

Artikel kommentieren

Kommentar von Tischi (2007-06-07 18:06:15):
ohne die funktion getmicrotime kann man nichts messen :)


function getmicrotime(){
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
return ($mtime);
}

Kommentar von Ingo Busch (2007-06-09 00:21:34):
Netter Artikel, von der Art habe ich schon mehrere gelesen, aber noch keinen so kurzen und verständlichen.

Also bei mir ist die Variante mit FOR die schnellere. Wärend FOR im Bereich 0,00060 arbeitet ist while im Bereich 0,00071 und foreach bei 0,00089.

Eingesetzt ist es bei mehrdimmensionalen Arrays mit 20 Datensätzen und jeweils 5 Unterdatensätzen.

Gruß, Ingo

Kommentar von christian_r (2008-07-06 10:42:17):
Also das PHP-Manual sagt mir etwas anderes zu include und require.

Zitat: "Die include() Anweisung bindet die angegebene Datei ein und wertet diese aus.

Die untenstehende Dokumentation gilt ebenso für require(). Diese beiden Konstrukte sind in jeder Hinsicht gleichwertig mit der einen Ausnahme: der Umgang mit Fehlern. include() erzeugt ein Warning während require() in einem Fatal Error endet. Mit anderen Worten, verwenden Sie require(), wenn Sie möchten, dass eine fehlende Datei die Ausführung ihres Skripts beendet. include() verhält sich anders, ihr Skript wird weiterhin ausgeführt."

Kommentar von huschi (2008-07-06 14:53:33):
@christian_r:
Einmal kurz in die Doku von "require()" geschaut und dort steht zusätzlich der Hinweiß:
"Da dies ein Sprachkonstrukt und keine Funktion ist, deshalb können Sie dieses nicht mit Variablenfunktionen verwenden."

Dies zeigt, daß "require()" zur Kompilerzeit und nicht zur Laufzeit (wie eben "include()") ausgeführt wird. Daher auch der Performance-Gewinn.