Nein, aber ja, aber vielleicht, aber vielleicht andersherum, aber nein.
Wie bereits erwähnt, ist der Ausdruck (unter der Annahme einer Sprache, in der die Addition linksassoziativ ist, z. B. C, C ++, C # oder Java) ((1 + 2) + 3)
genau gleichbedeutend mit 1 + 2 + 3
. Es sind verschiedene Arten, etwas in den Quellcode zu schreiben, das keine Auswirkung auf den resultierenden Maschinencode oder Bytecode hätte.
In beiden Fällen wird das Ergebnis eine Anweisung sein, z. B. zwei Register und dann ein drittes hinzuzufügen oder zwei Werte von einem Stapel zu nehmen, sie hinzuzufügen, zurückzuschieben, sie und ein anderes zu nehmen und sie hinzuzufügen oder drei Register hinzuzufügen eine einzelne Operation oder eine andere Möglichkeit, drei Zahlen zu summieren, je nachdem, was auf der nächsten Ebene am sinnvollsten ist (Maschinencode oder Bytecode). Im Fall von Byte-Code wird dieser wiederum wahrscheinlich eine ähnliche Umstrukturierung erfahren, z. B. das IL-Äquivalent davon (das wäre eine Reihe von Ladevorgängen für einen Stapel, und Popping-Paare zum Hinzufügen und anschließenden Zurückschieben des Ergebnisses). Dies würde nicht zu einer direkten Kopie dieser Logik auf Maschinencodeebene führen, sondern zu etwas Sinnvollerem für die betreffende Maschine.
Aber Ihre Frage hat noch etwas mehr zu bieten.
Im Fall eines vernünftigen C-, C ++ -, Java- oder C # -Compilers würde ich erwarten, dass das Ergebnis der beiden von Ihnen angegebenen Anweisungen genau die gleichen Ergebnisse liefert wie:
int a = 6;
Warum sollte der resultierende Code Zeit mit der Berechnung von Literalen verschwenden? Keine Änderungen am Status des Programms verhindern das Ergebnis des 1 + 2 + 3
Seins. Daher 6
sollte dies im ausgeführten Code berücksichtigt werden. In der Tat, vielleicht nicht einmal das (je nachdem, was Sie mit diesen 6 machen, können wir das Ganze vielleicht wegwerfen; und selbst C # mit der Philosophie "Optimiere nicht stark, da der Jitter dies sowieso optimieren wird" wird beides produzieren das Äquivalent von ( int a = 6
oder wirf das Ganze einfach als unnötig weg).
Dies führt uns jedoch zu einer möglichen Erweiterung Ihrer Frage. Folgendes berücksichtigen:
int a = (b - 2) / 2;
/* or */
int a = (b / 2)--;
und
int c;
if(d < 100)
c = 0;
else
c = d * 31;
/* or */
int c = d < 100 ? 0 : d * 32 - d
/* or */
int c = d < 100 && d * 32 - d;
/* or */
int c = (d < 100) * (d * 32 - d);
(Beachten Sie, dass die letzten beiden Beispiele kein gültiges C # sind, während alles andere hier ist und sie in C, C ++ und Java gültig sind.)
Auch hier haben wir genau den gleichen Code in Bezug auf die Ausgabe. Da es sich nicht um konstante Ausdrücke handelt, werden sie beim Kompilieren nicht berechnet. Möglicherweise ist eine Form schneller als eine andere. Welche ist schneller? Das hängt vom Prozessor und möglicherweise von einigen eher willkürlichen Zustandsunterschieden ab (zumal wenn man schneller ist, ist es wahrscheinlich nicht viel schneller).
Und sie haben nichts mit Ihrer Frage zu tun , da es hauptsächlich um Unterschiede in der Reihenfolge geht, in der etwas konzeptionell getan wird.
In jedem von ihnen gibt es einen Grund zu der Annahme, dass einer schneller ist als der andere. Einzelne Dekremente können eine spezielle Anweisung haben und (b / 2)--
könnten in der Tat schneller sein als (b - 2) / 2
. d * 32
könnte vielleicht schneller produziert werden, indem man es d << 5
so d * 32 - d
schneller macht als d * 31
. Die Unterschiede zwischen den letzten beiden sind besonders interessant; In einem Fall kann ein Teil der Verarbeitung übersprungen werden, im anderen Fall wird jedoch die Möglichkeit einer Verzweigungsfehlvorhersage vermieden.
Wir haben also zwei Fragen: 1. Ist eine tatsächlich schneller als die andere? 2. Wandelt ein Compiler das langsamere in das schnellere um?
Und die Antwort ist 1. Es kommt darauf an. 2. Vielleicht.
Oder zu erweitern, es kommt darauf an, weil es auf den jeweiligen Prozessor ankommt. Sicherlich gab es Prozessoren, bei denen das naive Maschinencode-Äquivalent des einen schneller war als das naive Maschinencode-Äquivalent des anderen. Im Laufe der Geschichte des elektronischen Rechnens hat es auch keinen gegeben, der immer schneller war (insbesondere das Element der Verzweigungsfehlvorhersage war für viele nicht relevant, als CPUs ohne Pipeline häufiger auftraten).
Und vielleicht, weil es eine Reihe verschiedener Optimierungen gibt, die Compiler (und Jitter und Skript-Engines) durchführen, und obwohl einige in bestimmten Fällen vorgeschrieben sind, können wir im Allgemeinen einige logisch äquivalente Code-Teile finden, die diesen Anforderungen entsprechen Selbst der naivste Compiler hat genau die gleichen Ergebnisse und einige logisch äquivalente Code-Teile, bei denen selbst der raffinierteste Code für den einen Code schneller als für den anderen erzeugt (selbst wenn wir etwas völlig Pathologisches schreiben müssen, um unseren Standpunkt zu beweisen).
Es scheint vielleicht ein sehr kleines Optimierungsproblem zu sein,
Nein. Selbst mit komplizierteren Unterschieden als denen, die ich hier gebe, scheint es eine absolut winzige Angelegenheit zu sein, die nichts mit Optimierung zu tun hat. Wenn überhaupt, ist es eine Frage der Pessimisierung, da Sie den Verdacht haben, dass das schwer zu lesende ((1 + 2) + 3
langsamer sein könnte als das leichter zu lesende 1 + 2 + 3
.
Bei der Auswahl von C ++ anstelle von C # / Java / ... geht es jedoch nur um Optimierungen (IMHO).
Wenn es wirklich darum geht, C ++ gegenüber C # oder Java zu wählen, sollten die Leute ihre Kopie von Stroustrup und ISO / IEC 14882 brennen und den Speicherplatz ihres C ++ - Compilers freigeben, um Platz für weitere MP3s oder ähnliches zu lassen.
Diese Sprachen haben unterschiedliche Vorteile.
Eine davon ist, dass C ++ im Allgemeinen immer noch schneller und speicherfreundlicher ist. Ja, es gibt Beispiele, in denen C # und / oder Java schneller sind und / oder eine bessere Nutzung des Arbeitsspeichers während der gesamten Anwendungslebensdauer aufweisen. Diese werden immer häufiger, je besser die beteiligten Technologien sind Eine kleinere ausführbare Datei, die ihre Arbeit schneller erledigt und weniger Speicher benötigt als die entsprechende Datei in einer dieser beiden Sprachen.
Dies ist keine Optimierung.
Mit Optimierung wird manchmal gemeint, dass die Dinge schneller gehen. Das ist verständlich, denn oft, wenn wir wirklich von "Optimierung" sprechen, geht es tatsächlich darum, die Dinge schneller zu machen, und so ist eine Abkürzung für die andere geworden, und ich gebe zu, dass ich das Wort selbst so falsch verwende.
Das richtige Wort für "Dinge schneller machen" ist nicht Optimierung . Das richtige Wort ist hier Verbesserung . Wenn Sie ein Programm ändern und der einzige bedeutende Unterschied darin besteht, dass es jetzt schneller ist, es in keiner Weise optimiert ist, sondern nur besser.
Optimierung ist, wenn wir eine Verbesserung in Bezug auf einen bestimmten Aspekt und / oder einen bestimmten Fall vornehmen. Häufige Beispiele sind:
- In einem Anwendungsfall ist es jetzt schneller, in einem anderen langsamer.
- Es ist jetzt schneller, verbraucht aber mehr Speicher.
- Das Gedächtnis ist jetzt leichter, aber langsamer.
- Es ist jetzt schneller, aber schwieriger zu warten.
- Es ist jetzt einfacher zu warten, aber langsamer.
Solche Fälle wären gerechtfertigt, wenn zB:
- Der schnellere Anwendungsfall ist anfangs häufiger oder schwerwiegender beeinträchtigt.
- Das Programm war inakzeptabel langsam und wir haben viel RAM frei.
- Das Programm kam zum Stillstand, weil es so viel RAM verbrauchte, dass mehr Zeit für das Austauschen aufgewendet wurde als für die Ausführung seiner superschnellen Verarbeitung.
- Das Programm war inakzeptabel langsam, und der schwer zu verstehende Code ist gut dokumentiert und relativ stabil.
- Das Programm ist immer noch akzeptabel schnell, und die verständlichere Codebasis ist billiger zu warten und ermöglicht es, andere Verbesserungen leichter vorzunehmen.
Solche Fälle wären jedoch auch in anderen Szenarien nicht gerechtfertigt: Der Code wurde nicht durch ein absolut unfehlbares Qualitätsmaß verbessert, sondern in einer bestimmten Hinsicht, die ihn für eine bestimmte Verwendung geeigneter macht. optimiert.
Die Wahl der Sprache hat hier Auswirkungen, da sowohl die Geschwindigkeit, die Speichernutzung und die Lesbarkeit als auch die Kompatibilität mit anderen Systemen, die Verfügbarkeit von Bibliotheken, die Verfügbarkeit von Laufzeiten und die Laufzeitreife auf einem bestimmten Betriebssystem davon betroffen sein können (Für meine Sünden hatte ich irgendwie Linux und Android als meine Lieblingsbetriebssysteme und C # als meine Lieblingssprache, und während Mono großartig ist, stoße ich immer noch ziemlich darauf).
Die Aussage "C ++ anstelle von C # / Java / ... zu wählen, bedeutet nur, dass es sich um Optimierungen handelt" ist sinnvoll, wenn Sie der Meinung sind, dass C ++ wirklich scheiße ist, denn bei der Optimierung geht es um "besser trotz ..." und nicht um "besser". Wenn Sie der Meinung sind, dass C ++ trotz allem besser ist, müssen Sie sich als letztes Gedanken über solche winzigen möglichen Micro-Opts machen. In der Tat ist es wahrscheinlich besser, wenn Sie es überhaupt aufgeben. Happy Hacker sind eine Qualität, für die es sich zu optimieren lohnt!
Wenn Sie jedoch dazu neigen zu sagen, "Ich liebe C ++, und eines der Dinge, die ich daran liebe, ist das Herausdrücken zusätzlicher Zyklen", dann ist das eine andere Sache. Es ist immer noch so, dass sich Mikro-Opts nur dann lohnen, wenn sie eine reflexive Angewohnheit sind (das heißt, die Art und Weise, wie Sie dazu neigen, natürlich zu codieren, ist umso schneller, je langsamer sie sind). Ansonsten handelt es sich nicht einmal um eine vorzeitige Optimierung, sondern um eine vorzeitige Pessimisierung, die die Situation nur verschlimmert.