57 Stimmen

isset() vs strlen() - eine schnelle/klare Berechnung der Stringlänge

Ich bin auf diesen Code gestoßen...

if(isset($string[255])) {
    // too long
}

isset() ist zwischen 6 und 40 schneller als

if(strlen($string) > 255) {
    // too long
}

Der einzige Nachteil von isset() ist, dass der Code unklar ist - wir können nicht auf Anhieb erkennen, was getan wird (siehe Pekkas Antwort). Wir können isset() in eine Funktion einbinden, z.B. strlt($string,255), aber dann verlieren wir die Geschwindigkeitsvorteile von isset().

Wie kann man die schnellere Funktion isset() verwenden und gleichzeitig die Lesbarkeit des Codes beibehalten?

EDIT : Test zum Nachweis der Geschwindigkeit http://codepad.org/ztYF0bE3

strlen() over 1000000 iterations 7.5193998813629
isset() over 1000000 iterations 0.29940009117126

EDIT2: hier ist, warum isset() schneller ist

$string = 'abcdefg';
var_dump($string[2]);
Output: string(1) “c”

$string = 'abcdefg';
if (isset($string[7])){
     echo $string[7].' found!';
  }else{
     echo 'No character found at position 7!';
}

Dies ist schneller als die Verwendung von strlen(), weil " der Aufruf einer Funktion teurer ist als die Verwendung eines Sprachkonstrukts." http://www.phpreferencebook.com/tips/use-isset-instead-of-strlen/

EDIT3 : Mir wurde immer beigebracht, dass ich mich für die Mirco-Optimierung interessiere. Wahrscheinlich, weil ich zu einer Zeit unterrichtet wurde, als die Ressourcen auf Computern winzig waren. Ich bin offen für die Idee, dass es vielleicht nicht wichtig ist, es gibt einige gute Argumente dagegen in den Antworten. Ich habe eine neue Frage zu diesem Thema gestartet... https://stackoverflow.com/questions/6983208/is-micro-optimisation-important-when-coding

57voto

Roel Punkte 18798

OK, also habe ich die Tests durchgeführt, da ich kaum glauben konnte, dass die isset()-Methode schneller ist, aber ja, sie ist es, und zwar erheblich. Die isset()-Methode ist durchweg etwa 6 Mal schneller.

Ich habe versucht, mit Zeichenfolgen von verschiedenen Größen und laufen eine unterschiedliche Anzahl von Iterationen; die Verhältnisse bleiben die gleichen, und auch die gesamte laufende Länge durch die Art und Weise (für Zeichenfolgen von unterschiedlichen Größen), weil sowohl isset() und strlen() sind O(1) (was Sinn macht - isset muss nur einen Lookup in einem C-Array zu tun, und strlen() gibt nur die Größe zählen, die für die Zeichenfolge gehalten wird).

Ich habe im php-Quelltext nachgeschaut, und ich glaube, ich verstehe in etwa, warum. isset(), weil es keine Funktion, sondern ein Sprachkonstrukt ist, hat seinen eigenen Opcode in der Zend VM. Daher muss es nicht in der Funktionstabelle nachgeschlagen werden und es kann spezielleres Parameter-Parsing durchführen. Der Code ist in zend_builtin_functions.c für strlen() und zend_compile.c für isset(), für diejenigen, die es interessiert.

Um auf die ursprüngliche Frage zurückzukommen: Aus technischer Sicht sehe ich keine Probleme mit der isset()-Methode; aber imo ist sie für Leute, die nicht an das Idiom gewöhnt sind, schwieriger zu lesen. Darüber hinaus ist die isset()-Methode zeitlich konstant, während die strlen()-Methode O(n) ist, wenn man die Anzahl der in PHP eingebauten Funktionen variiert. Das heißt, wenn Sie PHP bauen und viele Funktionen statisch kompilieren, werden alle Funktionsaufrufe (einschließlich strlen()) langsamer sein, aber isset() wird konstant sein. Allerdings wird dieser Unterschied in der Praxis vernachlässigbar sein; ich weiß auch nicht, wie viele Funktionszeigertabellen verwaltet werden, ob also auch benutzerdefinierte Funktionen einen Einfluss haben. Ich meine mich zu erinnern, dass sie in einer anderen Tabelle stehen und daher für diesen Fall irrelevant sind, aber es ist schon eine Weile her, seit ich das letzte Mal wirklich damit gearbeitet habe.

Im Übrigen sehe ich keine Nachteile der isset()-Methode. Ich kenne keine anderen Methoden, um die Länge einer Zeichenkette zu ermitteln, wenn man mal von absichtlich komplizierten Methoden wie explode+count und dergleichen absieht.

Schließlich habe ich auch Ihren obigen Vorschlag getestet, isset() in eine Funktion zu verpacken. Dies ist sogar langsamer als die strlen()-Methode, da ein weiterer Funktionsaufruf und damit ein weiterer Hash-Tabellenabruf erforderlich ist. Der Overhead durch den zusätzlichen Parameter (für die zu prüfende Größe) ist vernachlässigbar, ebenso wie das Kopieren der Zeichenkette, wenn sie nicht per Referenz übergeben wird.

22voto

Pekka Punkte 429407

Ein Geschwindigkeitsunterschied ist dabei völlig unerheblich. Es handelt sich bestenfalls um ein paar Millisekunden.

Verwenden Sie den Stil, der für Sie und alle anderen, die an dem Code arbeiten, am besten lesbar ist. Ich persönlich würde mich für das zweite Beispiel entscheiden, da es im Gegensatz zum ersten Beispiel die Absicht (die Überprüfung der Länge einer Zeichenkette) absolut klar macht.

15voto

Boris Yankov Punkte 1529

Ihr Code ist unvollständig.

Hier, ich habe es für Sie repariert:

if(isset($string[255])) {
    // something taking 1 millisecond
}

gegen

if(strlen($string) > 255) {
    // something taking 1 millisecond
}

Jetzt haben Sie keine leere Schleife, sondern eine realistische Schleife. Nehmen wir an, es dauert 1 Millisekunde, um etwas zu tun.

Eine moderne CPU kann eine Menge Dinge in 1 Millisekunde erledigen - das ist selbstverständlich. Aber Dinge wie ein zufälliger Festplattenzugriff oder eine Datenbankabfrage dauern mehrere Millisekunden - ebenfalls ein realistisches Szenario.

Berechnen wir nun erneut die Zeitangaben:

realistic routine + strlen() over 1000000 iterations 1007.5193998813629
realistic routine + isset() over 1000000 iterations 1000.29940009117126

Sehen Sie den Unterschied?

5voto

NikiC Punkte 98746

Erstens möchte ich auf Folgendes hinweisen eine Antwort von Artefacto zu erklären, warum Funktionsaufrufe einen Mehraufwand gegenüber Sprachkonstrukten bedeuten.

Zweitens möchte ich Sie darauf hinweisen, dass XDebug die Leistung von Funktionsaufrufen stark verringert, so dass Sie bei der Ausführung von XDebug möglicherweise verworrene Zahlen erhalten. Referenz (Zweiter Abschnitt der Frage). In der Produktion (wo Sie hoffentlich kein XDebug installiert haben) ist der Unterschied also noch geringer. Er geht von 6x auf 2x zurück.

Drittens sollten Sie wissen, dass es zwar einen messbaren Unterschied gibt, dieser aber nur dann sichtbar wird, wenn dieser Code in einer engen Schleife mit Millionen von Iterationen läuft. In einer normalen Webanwendung wird der Unterschied nicht messbar sein, sondern im Rauschen der Varianz untergehen.

Viertens ist zu beachten, dass heutzutage die Entwicklungszeit viel teurer ist als die Serverlast. Ein Entwickler, der auch nur eine halbe Sekunde länger braucht, um zu verstehen, was der isset-Code bewirkt, ist viel teurer als die Einsparung bei der CPU-Last. Außerdem lässt sich die Serverlast weitaus besser durch Optimierungen einsparen, die tatsächlich einen Unterschied machen (wie Caching).

3voto

Ismael Miguel Punkte 41

Dies ist der letzte Test:

function benchmark_function($fn,$args=null)
{
    if(!function_exists($fn))
    {
        trigger_error("Call to undefined function $fn()",E_USER_ERROR);
    }

    $t = microtime(true);

    $r = call_user_func_array($fn,$args);

    return array("time"=>(microtime(true)-$t),"returned"=>$r,"fn"=>$fn);
}

function get_len_loop($s)
{
    while($s[$i++]){}
    return $i-1;
}
echo var_dump(benchmark_function("strlen","kejhkhfkewkfhkwjfjrw"))."<br>";
echo var_dump(benchmark_function("get_len_loop","kejhkhfkewkfhkwjfjrw"));

Zurückgegebene Ergebnisse:

LAUF 1:

array(3) { ["time"]=> float(2.1457672119141E-6) ["returned"]=> int(20) ["fn"]=> string(6) "strlen" } array(3) { ["time"]=> float(1.1920928955078E-5) ["returned"]=> int(20) ["fn"]=> string(12) "get_len_loop" }

LAUF 2:

array(3) { ["time"]=> float(4.0531158447266E-6) ["returned"]=> int(20) ["fn"]=> string(6) "strlen" } array(3) { ["time"]=> float(1.5020370483398E-5) ["returned"]=> int(20) ["fn"]=> string(12) "get_len_loop" }

LAUF 3:

array(3) { ["time"]=> float(4.0531158447266E-6) ["returned"]=> int(20) ["fn"]=> string(6) "strlen" } array(3) { ["time"]=> float(1.2874603271484E-5) ["returned"]=> int(20) ["fn"]=> string(12) "get_len_loop" }

LAUF 4:

array(3) { ["time"]=> float(3.0994415283203E-6) ["returned"]=> int(20) ["fn"]=> string(6) "strlen" } array(3) { ["time"]=> float(1.3828277587891E-5) ["returned"]=> int(20) ["fn"]=> string(12) "get_len_loop" }

LAUF 5:

array(3) { ["time"]=> float(5.0067901611328E-6) ["returned"]=> int(20) ["fn"]=> string(6) "strlen" } array(3) { ["time"]=> float(1.4066696166992E-5) ["returned"]=> int(20) ["fn"]=> string(12) "get_len_loop" }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X