Frage

Gibt es einen Unterschied in der Leistung zwischen i++ und ++i wenn der resultierenden Wert nicht verwendet wird?

War es hilfreich?

Lösung

Zusammenfassung:. Nein

i++ könnte möglicherweise langsamer als ++i, da der alte Wert von i Möglicherweise müssen Sie für eine spätere Verwendung gespeichert werden, aber in der Praxis alle modernen Compiler wird diese entfernt optimieren.

Wir können dies zeigen, indem sie auf den Code für diese Funktion suchen, beide mit ++i und i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

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

}

Die Dateien sind die gleichen, mit Ausnahme von ++i und i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Wir werden kompilieren sie, und auch die erzeugte Assembler erhalten:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

Und wir können sehen, dass sowohl das erzeugte Objekt und Assembler-Dateien identisch sind.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

Andere Tipps

Effizienz im Vergleich Absicht von Andrew Koenig:

  

Als erstes ist es keineswegs selbstverständlich, dass ++i effizienter ist als i++, zumindest dann, wenn Integer-Variablen betroffen sind.

Und:

  

So ist die Frage, die man stellen soll, ist nicht, welche diese beiden Operationen schneller ist, ist es, welche diese beiden Operationen mehr zum Ausdruck bringt genau, was Sie versuchen zu erreichen. Ich behaupte, dass, wenn Sie nicht den Wert des Ausdrucks verwendet wird, gibt nie einen Grund ist i++ statt ++i zu verwenden, weil es nie einen Grund, den Wert einer Variablen, erhöhen Sie die Variable zu kopieren und dann die Kopie wegzuwerfen .

Also, wenn der resultierende Wert nicht verwendet wird, würde ich ++i verwenden. Aber nicht, weil es effizienter ist: weil es richtig meine Absicht erklärt.

Eine bessere Antwort ist, dass ++i manchmal schneller sein wird, aber nie langsamer.

Jeder scheint, dass i zu werden unter der Annahme, ist ein regelmäßiger eingebauter Typ wie int. In diesem Fall gibt es keinen messbaren Unterschied sein.

Allerdings, wenn i komplexer Typ ist, dann können Sie auch einen messbaren Unterschied finden. Für i++ müssen Sie eine Kopie Ihrer Klasse machen, bevor sie erhöht wird. Je nachdem, was in einer Kopie beteiligt ist könnte es in der Tat langsamer sein, da mit ++it können Sie nur den Endwert zurück.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Ein weiterer Unterschied besteht darin, dass mit ++i Sie die Möglichkeit haben, einen Verweis anstelle eines Wertes zurückzugeben. Auch hier je nachdem, was bei der Herstellung einer Kopie des Objekts beteiligt ist dies langsamer sein könnte.

Ein reales Beispiel, wo dies die Verwendung von Iteratoren auftreten kann, wäre. einen Iterator Kopieren ist unwahrscheinlich, dass ein Flaschenhals in der Anwendung sein, aber es ist immer noch ein gute Praxis in die Gewohnheit zu bekommen ++i statt i++ der Verwendung, wenn das Ergebnis nicht beeinflusst wird.

Ein Blatt von Scott Meyers, effektiver c ++ Artikel . 6: Unterscheiden Sie zwischen Präfix und postfix Formen der Erhöhungs- und Verringerungs Operationen

Die Vorsilbe Version immer über die Postfix in Bezug auf Objekte bevorzugt, insbesondere in Bezug auf Iteratoren.

Der Grund dafür, wenn man sich die Anrufmuster der Operatoren.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

In diesem Beispiel der Suche ist es leicht zu sehen, wie der Präfix-Operator immer effizienter sein wird als die Postfix. Wegen der Notwendigkeit für ein temporäres Objekt in der Verwendung des Postfix.

Aus diesem Grund, wenn Sie sehen Beispiele Iteratoren verwenden sie immer das Präfix-Version.

Aber wie Sie für int die darauf hinweisen, gibt es praktisch keinen Unterschied, da der Compiler-Optimierung, die stattfinden kann.

Hier ist eine weitere Beobachtung, wenn Sie über Mikro-Optimierung Sorgen machen. Erniedrigen Schleifen ‚möglicherweise‘ effizienter sein als Schleife erhöht wird (abhängig von der Befehlssatzarchitektur zum Beispiel ARM), gegeben:

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

Auf jeder Schleife Sie Sie einen Befehl jeweils für:

  1. Hinzufügen 1 zu i.
  2. Vergleichen, ob i kleiner als ein 100.
  3. Eine bedingte Verzweigung, wenn i kleiner als ein 100.

Während ein Erniedrigen Schleife:

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

Die Schleife wird eine Anweisung für jede haben:

  1. Decrement i, die Einstellung der CPU-Register Status-Flag.
  2. Eine bedingte Verzweigung abhängig von CPU-Register Status (Z==0).

Natürlich funktioniert dies nur, wenn Erniedrigen auf Null!

Remembered von dem ARM-System-Entwicklerhandbuch.

Kurze Antwort:

Es gibt nie einen Unterschied zwischen i++ und ++i in Bezug auf Geschwindigkeit. Ein guter Compiler sollte nicht anderen Code in den beiden Fällen erzeugen.

Lange Antwort:

Was jede andere Antwort nicht zu erwähnen, dass der Unterschied zwischen ++i gegen i++ macht nur Sinn innerhalb des Ausdrucks gefunden wird.

Im Fall von for(i=0; i<n; i++) ist die i++ allein in ihrem eigenen Ausdruck: Es gibt eine Sequenz Punkt vor dem i++ und es ist ein nach. So ist die einzige erzeugte Maschinencode ist „i von 1 erhöhen“, und es ist gut definiert, wie dies in Bezug auf den Rest des Programms sequenziert. Also, wenn Sie es ++ Präfix ändern würde, ist es nicht im geringsten egal wäre, würden Sie noch erhalten nur den Maschinencode „erhöhen i von 1“.

Die Unterschiede zwischen ++i und i++ nur Angelegenheiten in Ausdrücken wie array[i++] = x; gegen array[++i] = x;. Manche mögen argumentieren und sagen, dass die Postfix in solchen Operationen wird langsamer sein, weil das Register, in dem i befindet später neu geladen werden müssen. Aber dann beachten Sie, dass der Compiler kostenlos Ihre Anweisungen in irgendeiner Weise bestellen es gefällt, solange es nicht „das Verhalten der abstrakten Maschine brechen“, wie der C-Standard es nennt.

Während Sie also davon ausgehen können, dass array[i++] = x; in Maschinencode übersetzt wird, wie:

  • Wert speichern von i im Register A.
  • Speicheradresse des Arrays in Register B.
  • Fügen Sie A und B, speichern Ergebnisse in A.
  • An dieser neuen Adresse durch A, speichert den Wert von x.
  • Wert speichern von i im Register A // ineffizient, da zusätzliche Anweisung hier, wir haben bereits diese einmal.
  • Increment Register A.
  • Speichern Register A in i.

Der Compiler könnte auch den Code produziert effiziente, wie zum Beispiel:

  • Wert speichern von i im Register A.
  • Speicheradresse des Arrays in Register B.
  • Fügen Sie A und B, speichern Ergebnisse in B.
  • Increment Register A.
  • Speichern Register A in i.
  • ... // Rest des Codes.

Nur weil Sie als ein C-Programmierer, dass der Postfix ++ passiert am Ende zu denken, trainiert wird, wird der Maschinencode muss nicht auf diese Weise bestellt werden.

So gibt es keinen Unterschied zwischen Präfix und Postfix ++ in C. Nun, was Sie als C-Programmierer sollten variieren von sein, Menschen, die inkonsistent Präfix in einigen Fällen und Postfix in anderen Fällen verwenden, ohne Begründung, warum. Dies deutet darauf hin, dass sie sicher sind, wie C arbeitet oder dass sie eine falsche Kenntnisse der Sprache haben. Dies ist immer ein schlechtes Zeichen, tut es wiederum deuten darauf hin, dass sie andere fragwürdige Entscheidungen in ihrem Programm machen, basierend auf Aberglauben oder „religiöse Dogmen“.

„Präfix ++ ist immer schneller“ ist in der Tat eine solche falschen Dogma, das gemeinsam ist unter C-Programmierer Möchtegern-.

Bitte lassen Sie sich nicht die Frage, „was man schneller ist“ der entscheidende Faktor davon zu verwenden. Die Chancen stehen gut du bist nie so viel kümmern gehen, und außerdem Programmierer Lesezeit ist viel teurer als Maschinenzeit.

Verwenden Sie je nachdem, was am meisten Sinn für die menschliche macht den Code zu lesen.

Zu allererst:. Der Unterschied zwischen i++ und ++i ist vernachlässigbare in C


Zu den Details.

1. Das bekannte C ++ Ausgabe: ++i ist schneller

In C ++ ++i ist effizienter iff i ist eine Art eines Objekts mit einer überladenen Inkrementoperator.

Warum?
In ++i wird das Objekt zuerst inkrementiert wird, und kann anschließend als const Bezug auf jede andere Funktion übergeben. Dies ist nicht möglich, wenn der Ausdruck foo(i++) ist, weil jetzt der Zuwachs getan werden muss, bevor foo() genannt wird, aber der alte Wert muss auf foo() weitergegeben werden. Folglich wird der Compiler eine Kopie i machen gezwungen, bevor sie den Inkrementoperator auf dem Original ausgeführt wird. Die zusätzlichen Konstruktor / Destruktor Anrufe sind das Schlimme.

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

2. Die wenig bekannte Tatsache: i++ können schneller

Wenn kein Konstruktor / Destruktor aufgerufen werden muss, die in C immer der Fall ist, ++i und i++ sollen gleich schnell sein, nicht wahr? Nein, sie sind praktisch gleich schnell, aber es können kleine Unterschiede, die die meisten anderen Beantworter um den falschen Weg.

Wie kann schneller i++ sein?
Der Punkt ist, Datenabhängigkeiten. Wenn der Wert aus dem Speicher geladen werden muss, zwei nachfolgende Operationen müssen mit ihm getan werden, erhöht wird es, und es zu benutzen. Mit ++i muss die Inkrementierung getan werden vor kann der Wert verwendet werden. Mit i++ hat die Verwendung nicht, hängt von der Schrittweite und die CPU kann die Verwendung Betrieb parallel , um den Zuwachs Vorgang durchführt. Der Unterschied ist, höchstens einen CPU-Zyklus, so ist es wirklich vernachlässigbar, aber es ist da. Und es ist umgekehrt dann viele erwarten würden.

@ Mark Auch wenn der Compiler dabei ist die (Stack basiert) temporäre Kopie der Variablen und gcc (in neueren Versionen) zu optimieren weg erlaubt ist, Das bedeutet nicht, alle Compiler tun immer so.

Ich habe es getestet nur mit den Compiler sind in unserem aktuellen Projekt verwenden und 3 von 4 Sie es nicht optimieren.

übernehmen Sie nie der Compiler wird es richtig, vor allem, wenn die möglicherweise schneller, aber nie langsamer Code ist so einfach zu lesen.

Wenn Sie nicht über eine wirklich dumme Implementierung von einem der Betreiber in Ihrem Code haben:

Alwas bevorzugen ++ i über i ++.

In C, kann die Compiler sie im Allgemeinen optimiert gleich sein, wenn das Ergebnis nicht verwendet wird.

Doch in C ++, wenn andere Typen verwenden, die ihre eigenen ++ Operatoren zur Verfügung stellen, ist das Präfix Version wahrscheinlich die Postfix-Version schneller sein als. Also, wenn Sie die Postfix-Semantik nicht brauchen, ist es besser, den Präfix-Operator zu verwenden.

Ich kann eine Situation denken, wo postfix langsamer als Präfix Schritt ist:

Stellen Sie sich einen Prozessor mit Registern A als Akkumulator verwendet, und es ist das einzige Register in vielen Anweisungen verwendet wird (einige kleine Mikrocontroller tatsächlich wie diese sind).

Nun stellen Sie das folgende Programm und ihre Übersetzung in eine hypothetische assembly:

Präfix Schritt:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Postfix Schritt:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

Beachten Sie, wie der Wert von b gezwungen wurde, neu geladen werden. Mit Präfix Schritt kann der Compiler erhöht nur den Wert und die voran geht sie mit der Verwendung, möglicherweise vermeidet es neu zu laden, da der Sollwert bereits in dem Register nach dem Zuwachs ist. Doch mit Postfix Schritt, die Compiler hat mit zwei Werten beschäftigen, einem alten und einen dem erhöhten Wert, wie ich oben Ergebnissen in einem weiteren Speicherzugriff zeigen.

Natürlich, wenn der Wert des Zuwachses nicht verwendet wird, wie eine einzige i++; Aussage kann der Compiler (und nicht) einfach eine Inkrementbefehl erzeugen, unabhängig von Postfix oder Präfix-Nutzung.


Als Randbemerkung, würde ich zu erwähnen, dass ein Ausdruck, in dem ein b++ ist einfach nicht (durch Zugabe eines ++b zum Beispiel) ohne zusätzlichen Aufwand zu einem mit - 1 umgewandelt werden. So Vergleich der beiden, wenn sie Teil eines Ausdrucks sind nicht wirklich gültig ist. Oft, wo Sie verwenden b++ innerhalb eines Ausdrucks Sie nicht ++b verwenden können, so dass selbst wenn ++b potenziell effizienter wäre, wäre es einfach falsch. Ausnahme ist natürlich, wenn der Ausdruck für sie bettelt (zB a = b++ + 1;, die geändert werden kann, um a = ++b;).

ich immer vorziehen, Pre-Zuwachs, aber ...

Ich wollte darauf hinweisen, dass auch im Fall der Operator ++ Funktion aufrufen, wird der Compiler in der Lage sein, die vorübergehend zu optimieren weg, wenn die Funktion inlined wird. Da der Operator ++ in der Regel kurz und häufig im Header implementiert, ist es wahrscheinlich, inlined zu erhalten.

Also, für praktische Zwecke, gibt es wahrscheinlich nicht viel Unterschied zwischen der Leistung der beiden Formen. Allerdings habe ich immer lieber Prä-Inkrement, da es besser scheint direkt auszudrücken, was ich "zu sagen versuche, anstatt auf dem Optimierer angewiesen, es herauszufinden.

Auch weniger die Optmizer gibt wahrscheinlich zu tun, bedeutet, dass der Compiler schneller ausgeführt wird.

Meine C ist ein wenig rostig, so entschuldige ich mich im Voraus. Speedwise, kann ich die Ergebnisse verstehen. Aber, ich bin verwirrt, wie beide Dateien kamen zu dem gleichen MD5-Hash-out. Vielleicht machen Sie das gleiche für eine Schleife läuft, würde aber nicht die folgenden zwei Codezeilen unterschiedliche Montage erzeugen?

myArray[i++] = "hello";

vs

myArray[++i] = "hello";

Die erste schreibt den Wert in das Array, dann i inkrementiert. Die zweiten Stufen i schreibt dann in das Array. Ich bin kein Montage-Experte, aber ich sehe einfach nicht, wie die gleiche ausführbare würde durch diese zwei verschiedenen Zeilen Code erzeugt werden.

Nur meine zwei Cent.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top