Optimierung durch weniger Funktionsaufrufe

Jeder Funktionsaufruf in Ihrem PHP-Script kostet Zeit und damit Performance. Deshalb sollte bei der Programmierung darauf geachtet werden, überflüssige Aufrufe von Funktionen zu vermeiden. Das kann z.B. durch Speicherung des Rückgabewertes einer Funktion in einer Variable geschehen, um die Funktion nur einmalig aufrufen zu müssen und in der Folge auf den Wert der Variable zurückzugreifen. Ebenfalls positiv auf die Performance wirkt sich in den meisten Fällen die Wahl der „richtigen” Funktion aus. Gibt es eine PHP-Funktion, die dasselbe bewirkt wie zwei oder mehrere verkettete Funktionen, ist die Variante mit nur einer Funktion vorzuziehen.

Anhand von einigen kleinen Beispiel-Scripts möchten wir Ihnen auf praktische Art und Weise verdeutlichen, wie sich durch die Minimierung der Anzahl Funktionsaufrufe die Performance verbessern lässt.

Funktion als zweiter Ausdruck in for()-Schleife

Code:

<?php
for ($i = 0;$i < 1000;$i++) {
    $test[] = "Beispiel";
}
for ($i = 0;$i < count($test);$i++) {
    echo $test[$i];
}
?>

Erläuterung:
In der ersten for()-Schleife werden einem $test-Array 1.000 Werte zugewiesen. Innerhalb der zweiten Schleife werden die zugewiesenen Werte ausgegeben, solange die inkrementierte Variable $i kleiner als count($test) ist. Dabei wird 1.000mal die Funktion count() aufgerufen.

Variable als zweiter Ausdruck in for()-Schleife

Code:

<?php
for ($i = 0;$i < 1000;$i++) {
    $test[] = "Beispiel";
}
$count = count($test);
for ($i = 0;$i < $count;$i++) {
    echo $test[$i];
}
?>

Erläuterung:
Im Unterschied zum ersten Beispiel-Script wird die count() nicht 1.000mal, sondern nur einmalig aufgerufen und der Rückgabewert in der Variable $code gespeichert. Beim Durchlaufen der zweiten for()-Schleife wird nicht mehr direkt auf das Ergebnis von count($test) zugegriffen, sondern auf den in $count gespeicherten Wert. Der Code bewirkt exakt das gleiche wie der Code des ersten Beispiel-Scripts.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
Funktion als zweiter Ausdruck in for()-Schleife39.93 [#/sec] (mean)25.047 [ms] (mean)
Variable als zweiter Ausdruck in for()-Schleife49.92 [#/sec] (mean)20.031 [ms] (mean)
25.0%
20.0%

Das zweite Script, bei dem die Funktion count() nur einmalig aufgerufen und der Rückgabewert in einer Variable gespeichert wird benötigt 20% weniger Ausführzeit als das erste Script, bei dem die count()-Funktion als zweiter Ausdruck der for()-Schleife 1.000mal aufgerufen wird.

Fazit

Durch eine simple Umstellung des Codes lässt sich Ausführzeit sparen und die Performance verbessern. Je größer der Rückgabewert von count() ist, desto größer ist auch die Zeitersparnis. Dabei ist die count()-Funktion als zweiter Ausdruck der for()-Schleife nur exemplarisch. Genauso kritisch sind Funktionsaufrufe innerhalb der Schleife dahingehend zu untersuchen, ob Sie nicht auch außerhalb der Schleife durchgefürt werden können.
 

Ausgabe der Zeit mit time()

Code:

<?php
for ($i = 0;$i < 1000;$i++) {
    echo time();
}
?>

Erläuterung:
Innerhalb einer for()-Schleife wird 1.000mal ein Timestamp via time() ausgegeben.

Ausgabe der Zeit in einer Variable

Code:

<?php
$time = time();
for ($i = 0;$i < 1000;$i++) {
    echo $time;
}
?>

Erläuterung:
Vor Durchlauf der for()-Schleife wird der Variablen $time der Wert von time() zugewiesen. Innerhalb der Schleife wird der Zeitstempel nicht mehr mit time(), sondern dem Wert von $time ausgegeben. Davon ausgehend, dass Abweichungen von 1-2 Sekunden keine Rolle spielen, bewirken beide Beispiel-Scripte das gleiche.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
Ausgabe der Zeit mit time()50.75 [#/sec] (mean)19.703 [ms] (mean)
Ausgabe der Zeit in einer Variable60.95 [#/sec] (mean)16.406 [ms] (mean)
20.1%
16.7%

Das Script mit Zwischenspeicherung des Zeitstempels in der Variablen $zeit spart gegenüber dem ersten Script 16,7% der Ausführzeit.

Fazit

Ab PHP 5.1 steht der bei Start der Ausführung des Scripts aktuelle Zeitstempel in der Variablen $_SERVER['REQUEST_TIME'] zur Verfügung. Statt der von uns im Beispiel gewählten $time-Variable kann somit auf $_SERVER['REQUEST_TIME'] zugegriffen werden und auf time() komplett verzichtet werden.

Verwenden Sie if (!isset($_SERVER['REQUEST_TIME'])){$_SERVER['REQUEST_TIME']=time();}, wenn nicht sicher ist, ob PHP 5.1 oder höher auf dem Server installiert ist oder noch besser:

echo $_SERVER[‚REQUEST_TIME‘] mit PHP 5.2.x

Code:

<?php
for ($i = 0;$i < 1000;$i++) {
    echo $_SERVER['REQUEST_TIME'];
}
?>

Erläuterung:
Bei unserem Benchmark-Test steht PHP 5.2.x und somit die Variable $_SERVER['REQUEST_TIME'] zur Verfügung. Diese wird innerhalb einer Schleife 1.000mal via echo ausgegeben.

echo $time mit PHP 5.2.x

Code:

<?php
if (isset($_SERVER['REQUEST_TIME'])) {
    $time = $_SERVER['REQUEST_TIME'];
} else {
    $time = time();
}
for ($i = 0;$i < 1000;$i++) {
    echo $time;
}
?>

Erläuterung:
Sofern die Variable $_SERVER['REQUEST_TIME'] zur Verfügung steht, wird ihr Wert der Variablen $time zugewiesen. Andernfalls wird $time der Rückgabewert von time() zugewiesen.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
echo $_SERVER[‚REQUEST_TIME‘] mit PHP 5.2.x226.95 [#/sec] (mean)4.406 [ms] (mean)
echo $time mit PHP 5.2.x250.98 [#/sec] (mean)3.984 [ms] (mean)
10.6%
9.6%

Die sowohl mit PHP 4 als auch PHP 5 funktionsfähige Variante mit der Zwischenspeicherung des Zeitstempels in der Variablen $time spart rund 10% der Ausführzeit.

Fazit

Die Speicherung des Zeitwertes in einer Variablen wie der in unserem Beispiel $time genannten bringt auch dann Zeitersparnis, wenn die Variable $_SERVER['REQUEST_TIME'] zur Verfügung steht. Dies gilt natürlich nur, wenn der aktuelle Zeitstempel an mehreren Stellen im Script verwendet wird.

includieren von 10 kleinen Dateien

Code:

<?php
for ($i = 0;$i < 10;$i++) {
    $dneu = "<?n";
    for ($j = 0;$j < 100;$j++) {
        $dneu.= "\$test_" . $i . "[" . $j . "]="Beispiel";n";
    }
    $datei = "beispiel_" . $i . ".php";
    touch($datei);
    $dp = fopen($datei, "w");
    $dneu.= " ?>";
    fwrite($dp, $dneu);
    fclose($dp);
}
for ($i = 0;$i < 1000;$i++) {
    for ($j = 0;$j < 10;$j++) {
        $datei = "beispiel_" . $j . ".php";
        include ($datei);
    }
}
?>

Erläuterung:
Innerhalb der ersten ineinander verschachtelten for()-Schleifen werden 10 Dateien erstellt und in die Dateien jeweils Arrays mit 100 Werten in die Dateien geschrieben. In den darauffolgenden ineinander verschachtelten for()-Schleifen werden die erstellten Dateien 1.000mal includiert.

includieren einer großen Dateien

Code:

<?php
$dneu2 = "<?n";
for ($i = 0;$i < 10;$i++) {
    $dneu = "<?n";
    for ($j = 0;$j < 100;$j++) {
        $dneu2.= "\$test_" . $i . "[" . $j . "]=\"Beispiel\";\n";
        $dneu.= "\$test_" . $i . "[" . $j . "]=\"Beispiel\";\n";
    }
    $datei = "beispiel_" . $i . ".php";
    touch($datei);
    $dp = fopen($datei, "w");
    $dneu2.= " ?>";
    fwrite($dp, $dneu2);
    fclose($dp);
}
$datei2 = "beispiel.php";
touch($datei2);
$dp = fopen($datei2, "w");
$dneu.= " ?>";
fwrite($dp, $dneu);
fclose($dp);
for ($i = 0;$i < 1000;$i++) {
    $datei = "beispiel.php";
    include ($datei);
}
?>

Erläuterung:
Um realistische Vergleichswerte für die beiden Scripte zu erhalten, werden auch in diesem Code zunächst 10 Dateien mit je 100 enthaltenen Variablen erstellt. Zusätzlich wird eine elfte Datei erstellt, in die alle 1.000 in den 10 einzelnen Dateien enthaltenen Werte geschrieben werden. Beim 1.000maligem Durchlaufen der darauffolgenden Schleife werden im Unterschied zum ersten Script nicht die 10 Dateien mit je 100 Werten, sondern die eine Datei mit 1.000 enthaltenen Werten includiert.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
includieren von 10 kleinen Dateien0.11 [#/sec] (mean)9419.531 [ms] (mean)
includieren einer großen Dateien1.10 [#/sec] (mean)910.156 [ms] (mean)
90.0%
90.3%

Das Script, bei dem nur eine große Datei includiert wird ist 10mal so schnell wie das Script, bei dem 10 kleine Dateien includiert werden.

Fazit

Auch beim includieren von Dateien ist „weniger mehr”. Jeder eingesparte Aufruf von include() wirkt sich positiv auf die Performance Ihrer Scripte aus.
 

Anmerkung:
Die Tests wurden mit dem Apache HTTP server benchmarking tool ab auf einem im Februar 2017 nicht mehr dem Stand der Technik entsprechenden Server durchgeführt. Die RPS und TPR der getesteten PHP-Scripte sollten auf einem im Produktiv-Einsatz befindlichen Webserver deutlich besser sein.

Nächster Tipp