Ich habe diesen Code (meine strlen Funktion)
%Vor% Doing while (*str++)
, wie unten gezeigt, ist die Programmlaufzeit viel größer:
Ich mache das, um den Code zu untersuchen
%Vor% Im ersten Fall beträgt die Ausführungszeit etwa 6,7 Sekunden, während die Zeit im zweiten Fall (mit *str++
) etwa 10 Sekunden beträgt!
Warum so viel Unterschied?
Wahrscheinlich, weil der Post-Inkrement-Operator (der in der Bedingung der while
-Anweisung verwendet wird) eine temporäre Kopie der Variablen mit ihrem alten Wert enthält.
Was while (*str++)
wirklich bedeutet, ist:
Wenn Sie dagegen str++;
als eine einzelne Anweisung in den Rumpf der while-Schleife schreiben, befindet es sich in einem void-Kontext, daher wird der alte Wert nicht abgerufen, weil er nicht benötigt wird.
Um zusammenzufassen, haben Sie im Fall *str++
eine Zuweisung, 2 Inkremente und einen Sprung in jeder Iteration der Schleife. Im anderen Fall hast du nur 2 Inkremente und einen Sprung.
Dies hängt von Ihrem Compiler, den Compiler-Flags und Ihrer Architektur ab. Mit Apples LLVM gcc 4.2.1 erhalte ich keine merkliche Leistungsänderung zwischen den beiden Versionen, und das sollte auch nicht sein. Ein guter Compiler würde die *str
-Version in etwas wie
IA-32 (AT & amp; T Syntax):
%Vor% Die *str++
-Version könnte genau gleich kompiliert werden (da Änderungen an str
nicht sichtbar sind außerhalb von slen
, wenn das Inkrement tatsächlich auftritt, ist dies nicht wichtig), oder der Körper der Schleife könnte sein:
Andere haben bereits einige hervorragende Kommentare geliefert, einschließlich der Analyse des generierten Assembler-Codes. Ich empfehle dringend, dass Sie sie sorgfältig lesen. Wie sie gezeigt haben, kann diese Art von Frage nicht ohne irgendeine Quantifizierung beantwortet werden, also lasst uns ein bisschen damit spielen.
Zuerst werden wir ein Programm brauchen. Unser Plan ist folgender: Wir werden Strings erzeugen, deren Länge Potenzen von zwei sind, und alle Funktionen der Reihe nach versuchen. Wir laufen einmal durch, um den Cache zu primen, und dann 4096 Iterationen getrennt mit der höchsten verfügbaren Auflösung. Sobald wir fertig sind, werden wir einige grundlegende Statistiken berechnen: min, max und den einfach gleitenden Durchschnitt und dump es. Wir können dann eine rudimentäre Analyse machen.
Zusätzlich zu den zwei Algorithmen, die Sie bereits gezeigt haben, werde ich eine dritte Option zeigen, die überhaupt keinen Zähler verwendet, sondern sich auf eine Subtraktion stützt, und ich werde die Dinge durcheinander mischen, indem ich mich einschlage std::strlen
, nur um zu sehen, was passiert. Es wird ein interessanter Wermutstropfen sein.
Durch die Magie des Fernsehens ist unser kleines Programm bereits geschrieben, also kompilieren wir es mit gcc -std=c++11 -O3 speed.c
und wir bekommen Cranking, indem wir Daten erzeugen. Ich habe zwei separate Graphen gemacht, einen für Strings mit einer Größe von 32 bis 8192 Bytes und einen anderen für Strings mit einer Größe von 16384 bis hin zu 1048576 Bytes. In den folgenden Diagrammen ist die Y-Achse die Zeit, die in Nanosekunden verbraucht wird und die X-Achse zeigt die Länge der Zeichenfolge in Bytes an.
Sehen wir uns kurz die Leistung für "kleine" Strings von 32 bis 8192 Bytes an:
Jetzt das ist interessant. Die std::strlen
-Funktion übertrifft nicht nur alles auf der ganzen Linie, sie tut es auch mit Begeisterung, da ihre Leistung viel stabiler ist.
Ändert sich die Situation, wenn wir größere Strings von 16384 bis hin zu 1048576 Bytes betrachten?
Sortieren. Der Unterschied wird noch ausgeprägter. Wie unsere individuell geschriebenen Funktionen auf den Punkt gebracht haben, funktioniert std::strlen
weiterhin hervorragend.
Eine interessante Beobachtung ist, dass Sie die Anzahl der C ++ - Anweisungen (oder sogar die Anzahl der Assembly-Anweisungen) nicht unbedingt in die Performance übersetzen müssen, da Funktionen, deren Körper aus weniger Anweisungen bestehen, manchmal länger dauern.
Eine noch interessantere - und wichtige Beobachtung ist es zu bemerken, wie gut die Funktion str::strlen
funktioniert.
Erste Schlussfolgerung: erfinden Sie das Rad nicht neu. Verwenden Sie die Standardfunktionen, die Ihnen zur Verfügung stehen. Sie sind nicht nur bereits geschrieben, sondern sie sind sehr, sehr stark optimiert und werden mit hoher Wahrscheinlichkeit alle Möglichkeiten übertreffen, die Sie schreiben können, es sei denn, Sie sind Agner Fog .
Zweite Schlussfolgerung: Wenn Sie nicht über harte Daten von einem Profiler verfügen, dass ein bestimmter Codeabschnitt oder eine bestimmte Funktion in Ihrer Anwendung eine zentrale Rolle spielt, sollten Sie den Code nicht optimieren. Programmierer sind notorisch schlecht darin, Hot-Spots zu erkennen, indem sie auf eine Funktion auf hoher Ebene schauen.
Dritte Schlussfolgerung: Ziehen Sie algorithmische Optimierungen vor, um die Leistung Ihres Codes zu verbessern. Setze deine Gedanken in die Praxis und lass den Compiler die Bits mischen.
Ihre ursprüngliche Frage lautete: "Warum ist die Funktion slen2
langsamer als slen1
?" Ich könnte sagen, dass es nicht einfach ist, ohne viel mehr Informationen zu antworten, und selbst dann könnte es viel länger und komplizierter sein, als man sich wünscht. Statt dessen sage ich Folgendes:
Wen kümmert es warum? Warum beschäftigst du dich überhaupt damit? Verwenden Sie std::strlen
- was besser ist als alles, was Sie einrichten können - und gehen Sie weiter, um wichtigere Probleme zu lösen - weil ich sicher bin, dass nicht das größte Problem in Ihrer Anwendung ist / p>