532 Stimmen

Gibt es einen Leistungsunterschied zwischen i++ und ++i in C?

Gibt es einen Leistungsunterschied zwischen i++ y ++i wenn der resultierende Wert nicht verwendet wird?

20voto

Zuallererst: Der Unterschied zwischen i++ y ++i ist in C vernachlässigbar.


Zu den Details.

1. Das wohlbekannte C++-Problem: ++i ist schneller

In C++, ++i effizienter ist, wenn i ist eine Art Objekt mit einem überladenen Inkrement-Operator.

Warum?
En ++i wird das Objekt zunächst inkrementiert und kann anschließend als konstante Referenz an jede andere Funktion übergeben werden. Dies ist nicht möglich, wenn der Ausdruck foo(i++) denn jetzt muss das Inkrement erfolgen, bevor foo() aufgerufen wird, aber der alte Wert muss an foo() . Folglich ist der Compiler gezwungen, eine Kopie von i bevor es den Inkrement-Operator auf dem Original ausführt. Die zusätzlichen Konstruktor-/Destruktoraufrufe sind der schlechte Teil.

Wie bereits erwähnt, gilt dies nicht für Grundtypen.

2. Die wenig bekannte Tatsache: i++ Mai schneller sein

Wenn kein Konstruktor/Destruktor aufgerufen werden muss, was in C immer der Fall ist, ++i y i++ sollte doch gleich schnell sein, oder? Nein. Sie sind praktisch gleich schnell, aber es kann kleine Unterschiede geben, die die meisten anderen Antwortenden falsch verstanden haben.

Wie können i++ schneller sein?
Es geht um die Datenabhängigkeit. Wenn der Wert aus dem Speicher geladen werden muss, müssen zwei aufeinander folgende Operationen mit ihm durchgeführt werden, nämlich das Inkrementieren und die Verwendung des Wertes. Mit ++i muss die Inkrementierung durchgeführt werden vor kann der Wert verwendet werden. Mit i++ ist die Verwendung nicht vom Inkrement abhängig, und die CPU kann den Verwendungsvorgang durchführen parallel dazu auf die Inkrementierungsoperation. Der Unterschied beträgt höchstens einen CPU-Zyklus, ist also wirklich vernachlässigbar, aber er ist da. Und es ist andersherum, als viele erwarten würden.

19voto

tonylo Punkte 3220

Wenn Sie sich Sorgen um die Mikro-Optimierung machen, sollten Sie noch eine weitere Beobachtung machen. Dekrementierende Schleifen können "möglicherweise" effizienter sein als inkrementierende Schleifen (je nach Befehlssatzarchitektur, z. B. ARM), vorausgesetzt:

for (i = 0; i < 100; i++)

In jeder Schleife haben Sie jeweils eine Anweisung für:

  1. Hinzufügen von 1 à i .
  2. Vergleichen Sie, ob i kleiner ist als ein 100 .
  3. Eine bedingte Verzweigung, wenn i kleiner ist als ein 100 .

Eine dekrementierende Schleife:

for (i = 100; i != 0; i--)

Die Schleife enthält eine Anweisung für jedes der folgenden Elemente:

  1. Verringern i Setzen des CPU-Registerstatusflags.
  2. Eine bedingte Verzweigung in Abhängigkeit vom CPU-Registerstatus ( Z==0 ).

Das funktioniert natürlich nur, wenn auf Null dekrementiert wird!

Aus dem ARM System Developer's Guide.

10voto

Andy Lester Punkte 86147

Bitte lassen Sie sich nicht von der Frage "welcher ist schneller" leiten, wenn es darum geht, welchen Computer Sie verwenden. Die Chancen stehen gut, dass Sie sich nie so sehr dafür interessieren werden, und außerdem ist die Lesezeit eines Programmierers viel teurer als die Maschinenzeit.

Verwenden Sie das, was für den Menschen, der den Code liest, am sinnvollsten ist.

8voto

Andreas Punkte 1319

@Mark Auch wenn der Compiler die (auf dem Stack basierende) temporäre Kopie der Variablen wegoptimieren darf und gcc (in neueren Versionen) dies auch tut, bedeutet das nicht, dass alle Compiler werden dies immer tun.

Ich habe es gerade mit den Compilern getestet, die wir in unserem aktuellen Projekt verwenden, und 3 von 4 optimieren es nicht.

Gehen Sie nie davon aus, dass der Compiler es richtig macht, vor allem wenn der möglicherweise schnellere, aber niemals langsamere Code genauso leicht zu lesen ist.

Wenn Sie nicht eine wirklich dumme Implementierung eines der Operatoren in Ihrem Code haben:

Immer lieber ++i als i++.

8voto

daShier Punkte 2016

Ich habe die meisten Antworten hier und viele der Kommentare gelesen, und ich habe keinen Hinweis auf die eine Ein Beispiel, an das ich denken könnte, wo i++ ist effizienter als ++i (und vielleicht überraschenderweise --i war effizienter als i-- ). Das ist für C-Compiler für die DEC PDP-11!

Die PDP-11 verfügte über Assembleranweisungen für die Vordekrementierung eines Registers und die Nachdekrementierung, aber nicht umgekehrt. Die Anweisungen erlaubten es, jedes "Allzweck"-Register als Stapelzeiger zu verwenden. Wenn Sie also etwas verwenden wie *(i++) in einen einzigen Assembler-Befehl kompiliert werden könnte, während *(++i) konnte nicht.

Dies ist natürlich ein sehr esoterisches Beispiel, aber es bietet die Ausnahme, in der Post-Increment effizienter ist (oder ich sollte sagen war da es heutzutage keine große Nachfrage nach PDP-11 C-Code gibt).

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