Neulich habe ich ja schon die Auswirkungen von Konstanten mit Arrays in Bezug auf Speicherverbrauch verglichen. Nun ist es wieder Zeit für neue Performance-Erkenntnisse. Dieses Mal habe ich mir das Thema „preg_replace ist langsam!(?)“ rausgesucht, da ich herausfinden wollte, wie „böse“ wirklich die preg-Funktionen in Hinsicht auf Performance sind.
Mit preg-Funktionen muss man, wenn es denn geht, sparsam umgehen, sowas lernt man als PHP-Programmierer gleich von Anfang an. In der offiziellen PHP-Dokumentation steht es ja auch bei so ziemlich jeder preg-Funktion dabei, dass man sie nach Möglichkeit nicht nur für einfache Vergleiche (z.B. Ist String X in String Y enthalten) oder einfache Ersetzungen (z.B. ersetze „Hallo“ mit „Auf Wiedersehen“) benutzen soll. Das ist auch richtig so, da die Funktionen mit den regulären Ausdrücken nun mal „gewichtiger“ als die einfachen String-Funktionen (z.B. strpos, str_replace, etc.) sind. Aber inwiefern wirkt es sich auf die Laufzeit des Skripts aus? Ist jede Verwendung von preg_replace schon ein Performance-Killer? Ich habe mal ein paar Tests gemacht, die so sicherlich nicht auf den Alltag abzubilden sind, aber die Perfomance-Unterschiede auf gewisse Art darstellen sollen.
Zum Testen nehme ich zwei kurze Skripte, die, wie oben schon erwähnt, nicht sehr realitätsnah sind, aber hier ihren Zweck erfüllen. Skript Nummer 1 verwendet die Funktion preg_replace, Skript Nummer 2 gibt sich mit str_replace zufrieden.
Skript 1 – preg_replace
ini_set("memory_limit", "180M"); $intTimeStart = time(); $intMax = 100; $str = "Hallo %grausame% Welt"; $str2 = ""; for($i = 0; $i < $intMax; $i++) { $str2 = preg_replace("/%grausame%/i", "schöne", $str); } echo "\n\n" . memory_get_peak_usage() / 1024 / 1024 . " MB\n\n"; echo "\n\n" . time() - $intTimeStart . " s\n\n";
Skript 2 - str_replace
ini_set("memory_limit", "180M"); $intTimeStart = time(); $intMax = 100; $str = "Hallo %grausame% Welt"; $str2 = ""; for($i = 0; $i < $intMax; $i++) { $str2 = str_replace("%grausame%", "schöne", $str); } echo "\n\n" . memory_get_peak_usage() / 1024 / 1024 . " MB\n\n"; echo "\n\n" . time() - $intTimeStart . " s\n\n";
Die Variable $intMax stellt die Anzahl an "Ersetzungen" dar. Hier sind nun die Ergebnisse, wenn die Zahl entsprechend gesetzt wird.
$intMax = 100
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.0391 | 0 |
str_replace | 0.0391 | 0 |
$intMax = 10000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.0391 | 0 |
str_replace | 0.0391 | 0 |
$intMax = 100000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.0391 | 0 |
str_replace | 0.0391 | 0 |
$intMax = 1000000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.0391 | 2 |
str_replace | 0.0391 | 2 |
$intMax = 10000000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.0391 | 24 |
str_replace | 0.0391 | 15 |
Interessante Ergebnisse! Da immer dieselben Strings behandelt werden, ist es nicht verwunderlich, dass der Speicherverbrauch immer gleich bleibt. Aber auch die Laufzeit ändert sich (bei diesem einfachen Beispielskript) erst ab einer (verdammt) hohen Anzahl an Funktionsaufrufen!
Hieraus kann man wohl erkennen, dass auch einfache preg_replace-Ersetzungen bei weitem nicht das ganze Skript ausbremsen. Dennoch ist es natürlich "guter Stil", wenn man bei einfachen String-Operationen einen Bogen um die preg-Funktionen macht.
Nachtrag
Da war ich wohl etwas vorschnell! :-) Ich habe eben die Test noch mal wiederholt und dabei eine viel größere Zeichenkette genommen. Die Funktionen bleiben identisch, allerdings habe ich den Quelltext der Startseite von RVI-Media genommen und lasse nun zum Test alle div-Elemente durch span-Elemente ersetzen.
$intMax = 100
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.688 | 0 |
str_replace | 0.664 | 0 |
$intMax = 1000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.688 | 0 |
str_replace | 0.664 | 1 |
$intMax = 100000
Speicher in MB | Laufzeit in Sekunden | |
---|---|---|
preg_replace | 0.688 | 13 |
str_replace | 0.664 | 6 |
Bei größeren Zeichenketten ist also schon bei weitaus weniger Durchläufen eine viel größere Diskrepanz zwischen den Funktionen zu spüren. Dennoch bleibt es dabei, dass es wirklich erst ab einer (sehr) großen Anzahl an Ersetzungen spürbar wird.
Wow ein Benchmark ohne microtime.
Sachen wie 0 Sekunden im Vergleich zu 0 Sekunden oder 2 Sekunden zu 2 Sekunden sind wirklich sehr aufschlussreich!
Naja, das war leider nicht so nuetzvoll… Koenntest du es bitte mit microtime widerholen? :)
Hey Marco,
aus heutiger Sicht würde ich es vermutlich auch mit Mikrosekunden benchmarken (einfach der nerdigen Nachfrager wegen ;P) aber soweit ich mich erinnere hat es sich einfach nicht gelohnt um (für mich persönlich) mit dem Gerücht aufzuräumen, dass das die preg_-Sachen ganz viel langsamer wären. Und ob ein str_replace nun bei 1000000 Ersetzungen 0.1 Sekunde schneller ist als ein preg_replace interessiert mich heute auch immer noch nicht sonderlich.
Generell sollte man sich ja nicht auf irgendwelche Benchmarks aus dem Internet verlassen, die zu dem noch mit alten (PHP-)Versionen gemacht wurden, sondern bei Bedarf einfach in der eigenen entsprechenden Systemumgebung testen.
Sollte ich mal wieder Benchmarks veröffentlichen, dann aber natürlich in Mikrosekunden, danke für den Hinweis!