Überprüfen von Strings vor Ersetzen

Lässt sich wirklich die Performance von PHP-Scripten verbessern, wenn vor dem Ersetzen mit str_replace() geprüft wird, ob der zu ersetzende Teilstring im Gesamtstring enthalten ist? Wir haben diesen weit verbreiteten Optimierungs-Tipp einem Benchmark unterzogen.

String (2000 Zeichen) enthält Argument, ohne strpos()

Code:

<?php
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 20;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    $test = str_replace("f", "g", $string);
}
?>

Erläuterung:
Innerhalb der ersten for()-Schleife wird ein String mit einer Länge von 2.000 Zeichen generiert. Mittels der zweiten for()-Schleife wird eine Ersetzung via str_replace() vorgenommen.

String (2000 Zeichen) enthält Argument nicht, ohne strpos()

Code:

<?php
$teilstring = "m";
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 20;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    $test = str_replace("m", "g", $string);
}
?>

Erläuterung:
Im Unterschied zum ersten Beispiel-Script ist der zu ersetzende Teilstring im Gesamtstring nicht enthalten.

String (2000 Zeichen) enthält Argument, mit strpos()

Code:

<?php
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 20;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    if (strpos($string, "f") !== false) {
        $test = str_replace("f", "g", $string);
    }
}
?>

Erläuterung:
Der zu ersetzende Teilstring ist im Gesamtstring enthalten. Daher liefert die vor dem Ersetzen durchgeführte Prüfung via strpos() den Rückgabewert true und die Ersetzung via str_replace() wird durchgeführt.

String (2000 Zeichen) enthält Argument nicht, mit strpos()

Code:

<?php
$teilstring = "m";
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 20;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    if (strpos($string, "f") !== false) {
        $test = str_replace("m", "g", $string);
    }
}
?>

Erläuterung:
Der Teilstring ist im Gesamtstring nicht enthalten, die Überprüfung via strpos() fällt negativ aus und die Ersetzung mittels str_replace() kommt nicht zum Tragen.

String (5000 Zeichen) enthält Argument, ohne strpos()

Code:

<?php
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 50;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    $test = str_replace("f", "g", $string);
}
?>

Erläuterung:
Dieser Code entspricht dem ersten Beispiel-Script auf dieser Seite mit dem Unterschied, dass der String nun 5.000 Zeichen statt nur 2.000 Zeichen umfasst.

String (5000 Zeichen) enthält Argument nicht, ohne strpos()

Code:

<?php
$teilstring = "m";
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 50;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    $test = str_replace("m", "g", $string);
}
?>

Erläuterung:
Das Äquivalent zum zweiten Beispiel-Script mit einem 5.000 Zeichen umfassenden String.

String (5000 Zeichen) enthält Argument, mit strpos()

Code:

<?php
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 50;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    if (strpos($string, "f") !== false) {
        $test = str_replace("f", "g", $string);
    }
}
?>

Erläuterung:
Dies ist eine Kopie des dritten Beispiel-Scripts mit einem String der Länge 5.000.

String (5000 Zeichen) enthält Argument nicht, mit strpos()

Code:

<?php
$teilstring = "m";
$string = "abcdefghijk";
$gesamtstring = "";
for ($i = 0;$i < 50;$i++) {
    $gesamtstring.= $string;
}
for ($i = 0;$i < 10000;$i++) {
    if (strpos($string, "f") !== false) {
        $test = str_replace("m", "g", $string);
    }
}
?>

Erläuterung:
Im letzten Beispiel-Script wird der String mit einer Länge von 5.000 Zeichen erfolglos mit strpos() durchsucht. Die Ersetzung via str_replace() wird folglich nicht vorgenommen.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
String (2000 Zeichen) enthält Argument, ohne strpos()54.61 [#/sec] (mean)18.313 [ms] (mean)
40.6%
28.9%
String (2000 Zeichen) enthält Argument nicht, ohne strpos()61.48 [#/sec] (mean)16.266 [ms] (mean)
58.3%
36.8%
String (2000 Zeichen) enthält Argument, mit strpos()39.05 [#/sec] (mean)25.609 [ms] (mean)
0.6%
0.5%
String (2000 Zeichen) enthält Argument nicht, mit strpos()42.36 [#/sec] (mean)23.609 [ms] (mean)
9.1%
8.3%
String (5000 Zeichen) enthält Argument, ohne strpos()54.94 [#/sec] (mean)18.203 [ms] (mean)
41.5%
29.3%
String (5000 Zeichen) enthält Argument nicht, ohne strpos(59.76 [#/sec] (mean)16.734 [ms] (mean)
53.9%
35.0%
String (5000 Zeichen) enthält Argument, mit strpos()38.83 [#/sec] (mean)25.750 [ms] (mean)
String (5000 Zeichen) enthält Argument nicht, mit strpos()42.64 [#/sec] (mean)23.453 [ms] (mean)
9.8%
8.9%

Sämtliche Beispiel-Scripte, bei denen der String mittels strpos() durchsucht wurde, benötigen deutlich mehr Ausführzeit als die nur mit str_replace() ohne vorangegangene Überprüfung agierenden Scripte; unabhängig davon, ob der Teilstring im Gesamtstring enthalten ist oder nicht.

Fazit

Bei Strings entsprechender Länge fällt die Prüfung via strpos() tatsächlich kaum ins Gewicht. Bei Strings mit einer Länge von maximal 5.000 Zeichen kostet sie jedoch nur unnötig Zeit. Es gilt also zu berücksichtigen, dass der zu durchsuchende String deutlich länger als 5.000 Zeichen und die Wahrscheinlichkeit gering sein sollte, dass der zu ersetzende Teilstring im Gesamtstring enthalten ist. In der Praxis wird das selten der Fall sein.

 

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.

Optimierung bei der Suche eines Vorkommnisses im String

Optimierung bei der Suche eines Vorkommnisses im String

Bei der simplen Suche nach Vorkommnis eines Teilstrings in einem Gesamtstring stehen die beiden Funktionen strstr() und strpos bereit. Da strstr() im Erfolgsfall einen String zurückgibt und strpos() eine integere Zahl als Rückgabewert liefert, ist davon auszugehen, dass strpos() die schnellere Variante ist. Wir vergleichen die Ausführzeiten anhand von Benchmarks mit folgenden kleinen PHP-Scripts.