Performance-Steigerung durch Vermeidung des @-Operators

Der @-Operator ist nicht nur bei Anfängern der PHP-Programmierung sehr beliebt. Selbst WordPress ist gespickt mit dieser unsauberen Form der Programmierung. Ob Variablen definiert sind oder Dateien existieren muss nicht extra via isset() bzw. file_exists() abgefragt werden. Ein einfaches @-Zeichen vor dem Variablennamen oder der include()-Funktion und schon sind scheinbar alle Probleme gelöst.

Doch was so einfach klingt ist in Wirklichkeit eine weit verbreitete Unterschätzung dessen, wie viel der harmlose @-Operator tatsächlich kostet. Der von PHP betriebene Aufwand entspricht in etwa dem zweifachen Ausführen der ini_set()-Funktion.

Zur Veranschaulichung haben wir drei PHP-Scripte erstellt und sie einem Benchmark-Test unterzogen.

PHP-Script mit @-Operator

Code:

<?php
for ($i = 0;$i < 10000;$i++) { echo @$test; } ?>

Erläuterung:
Eine nicht definierte Variable $test soll via echo ausgegeben werden. Damit keine Fehlermeldung Notice undefined variable test erscheint, verwenden wir den @-Operator. Um ein aussagekräftiges Ergebnis zu erhalten, wird die Ausgabe in einer Schleife mit 10.000 Durchläufen ausgeführt.

PHP-Script mit ini_set()

Code:

<?php
for ($i = 0;$i < 10000;$i++) { $alt = ini_set("error_reporting", 0); echo $test; ini_set("error_reporting", $alt); } ?>

Erläuterung:
Dieser Code ist wohl nicht nur für erfahrene PHP-Programmierer ein Albtraum. Davon abgesehen, dass die PHP-Funktion ini_set() nach Möglichkeit vermieden werden sollte, würde Sie jeder annähernd auf Performance achtende Programmierer außerhalb der Schleife verwenden. Wir möchten mit diesem Code belegen, welch enormer Aufwand beim Interpretieren des @-Operators entsteht.

PHP-Script mit isset()

Code:

<?php
for ($i = 0;$i < 10000;$i++) { if (isset($test)) { echo $test; } } ?>

Erläuterung:
Die nicht definierte Variable $test würde via echo ausgegeben werden. Da jedoch die Überprüfung mit isset() false zurückgibt, erfolgt keine Ausgabe. Auch diese Zeile wird in einer Schleife 10.000mal aufgerufen.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
PHP-Script mit @-Operator28.47 [#/sec] (mean)35.125 [ms] (mean)
33.3%
24.8%
PHP-Script mit ini_set()21.41 [#/sec] (mean)46.703 [ms] (mean)
PHP-Script mit isset()199.38 [#/sec] (mean)5.016 [ms] (mean)
831.2%
89.3%

Das Ergebnis des Benachmark-Tests bestätigt unsere Vermutung: Unser Script mit dem @-Operator benötigt nur rund 25% weniger Zeit als der PHP-Code mit zweifachem Aufruf der ini_set()-Funktion. Die „saubere” Variante mit der Überprüfung via isset() ist fast 9mal schneller als das PHP-Script mit ini_set() und über 7mal schneller als der Code mit Verwendung des @-Operators.

Fazit

Es kann nur ein Fazit geben: Der @-Operator zur Unterdrückung von Fehlermeldungen ist bei der Programmierung mit PHP unbedingt zu vermeiden.

Nachdem wir nun erfahren haben, dass mit Vermeidung des @-Operators bei Variablen in nicht unerheblichem Umfang Zeit und Ressourcen gespart werden können, interessiert uns die mögliche Ersparnis beim includieren nicht vorhandener Dateien. Wir erstellen folgende beiden PHP-Scripte:

include() mit @-Operator

Code:

for ($i=0;$i@include("./beispiel.php");
}
?>

Erläuterung:
In einer for()-Schleife wird 10.000 mal versucht, eine nicht vorhandene beispiel.php-Datei zu includieren. Um die Fehlermeldung Warning: include(./beispiel.php) [function.include]: failed to open stream: No such file or directory zu unterdrücken, verwenden wir den @-Operator.

include() mit file_exists()

Code:

for ($i=0;$iif (file_exists("./beispiel.php")){include("./beispiel.php");}
}
?>

Erläuterung:
Vor Aufruf der include()-Funktion wird geprüft, ob die zu includierende Datei existiert. Da file_exists() in unserem Beispiel false zurückgibt, wird das include()-Statement nicht aufgerufen und kann entsprechend keine Fehlermeldung verursachen.

Ergebnis

 

Requests per second (RPS)Time per Request (TPR)Vergleich
PHP-Script mit @-Operator0.36 [#/sec] (mean)2799.563 [ms] (mean)
include() mit file_exists()0.50 [#/sec] (mean)1988.438 [ms] (mean)
38.9%
29.0%

Die Existenz einer zu includierenden Datei mit file_exists() vor Aufruf der include()-Funktion zu prüfen war in unserem Benchmark-Test 1,4mal schneller als der include()-Funktion den @-Operator voranzustellen.

Fazit

Da auch die file_exists() beim Interpretieren für PHP einen gewissen Aufwand bedeutet, ist der Unterschied nicht ganz so gravierend wie bei der simplen Ausgabe einer Variable in unserem ersten Benchmark. Aber auch der 29%ige Zeitgewinn verdeutlicht, dass der @-Operator unbedingt umgangen werden sollte.

Wir haben weitere Möglichkeiten der Optimierung getestet, die beim Includieren von Dateien, der Existenz nicht sicher ist in Betracht gezogen werden können. Lesen Sie hierzu: Optimierung von include und file_exists.

 

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.

Lesen Sie weiter…