Warum wird n++ schneller ausgeführt als n=n+1?

Lesezeit: 9 Minuten

In C-Sprache, Why does n++ schneller ausführen als n=n+1?

(int n=...;  n++;)
(int n=...;  n=n+1;)

Unsere Lehrerin hat diese Frage in der heutigen Klasse gestellt. (Das ist keine Hausaufgabe)

  • Wie hast du das herausgefunden? Auf welchem ​​Compiler/Betriebssystem/Plattform/Architektur?

    – mmx

    21. Mai 2010 um 18:59 Uhr

  • Im Allgemeinen nicht. Sie können keine sinnvolle Aussage machen, dass “a ist schneller als b” (wo a und b sind C-Ausdrücke). Es macht nur Sinn zu sagen, dass “a ist schneller als b auf Hardware c wenn mit Version kompiliert d des Compilers e mit Optimierungsflags f” (und ein paar andere Anforderungen auch).

    – Stefan Kanon

    21. Mai 2010 um 19:04 Uhr


  • Aus dem Beispiel geht hervor, was hier gefragt wird; Nur weil die Antwort offensichtlich erscheint, heißt das nicht, dass Sie sie schließen sollten. +1 für Wiedereröffnung.

    – bdonlan

    21. Mai 2010 um 19:15 Uhr

  • @Stephen: Der Fragesteller sollte also in der Antwort korrigiert werden. Es ist immer noch eine gute Frage, weil jemand anderes später auf die gleiche Fehlinformation stoßen könnte, und diese Frage wird helfen, sie zu sortieren

    – bdonlan

    21. Mai 2010 um 19:22 Uhr

  • @Stephen, auch wenn Sie wirklich der Meinung sind, dass die Frage keine falsche Aussage stellen sollte, können Sie sie gerne in “Ist n++ schneller als n = n + 1und warum?”. Das ist viel konstruktiver, als es einfach ohne Diskussion zu schließen 🙂

    – bdonlan

    21. Mai 2010 um 19:23 Uhr

Benutzer-Avatar
Betamoo

Das wäre wahr, wenn Sie an einem arbeiten “Steinzeit” Compiler…

Im Falle von “Steinzeit”:
++n ist schneller als n++ ist schneller als n=n+1

Maschine haben normalerweise increment x ebenso gut wie add const to x

  • Im Falle von n++Sie haben nur 2 Speicherzugriffe (lesen n, inc n, schreiben n )
  • Im Falle von n=n+1Sie haben 3 Speicherzugriffe (lesen n, lesen const, addieren n und const, schreiben n)

Aber der heutige Compiler konvertiert automatisch n=n+1 zu ++nund es wird mehr tun, als Sie sich vorstellen können !!

Auch auf den heutigen Out-of-Order-Prozessoren – trotz des Falls von “Steinzeit” Compiler-Laufzeit darf nicht beeinträchtigt werden überhaupt in vielen Fällen!!

Verwandt

  • @gcc: Nein, das solltest du NICHT sagen. Sie sollten sagen: “Es sollte überhaupt keinen Unterschied zwischen den beiden geben. Optimierende Compiler haben solche Dinge seit Jahrzehnten gehandhabt, seit zumindest der Bliss-11-Compiler, der in “Design of an Optimizing Compiler” von Wulf et al. beschrieben wird.”

    – John R. Strohm

    21. Mai 2010 um 19:14 Uhr

  • Auf dem Computer habe ich davon erfahren, ++n und n++ waren genau die gleiche Geschwindigkeit, es musste kein Laden-Inkrement-Speichern durchgeführt werden, und der einzige Geschwindigkeitsunterschied, den Sie sehen würden, hing davon ab, ob n in einem Register oder Speicher gespeichert wurde (nicht ob n gerade in einem Register war, sondern ob der zugewiesene Speicher in einem Register war oder nicht) zB move (n)+,r0 verschiebt n zum Register Null und inkrementiert n nach der Verschiebung. (VAX-11-Montage)

    – Stefan P

    21. Mai 2010 um 19:33 Uhr

  • +1, um die Frage tatsächlich auf nützliche Weise zu beantworten, anstatt nur zu sagen, dass sie gleich sind, was nützlich zu wissen ist, aber nicht die Informationen, die das OP wahrscheinlich wollte.

    – Ben Zotto

    21. Mai 2010 um 20:11 Uhr

  • Zu sagen, der Compiler wird “konvertieren n=n+1 zu ++n“ ist einfach falsch oder sogar Unsinn. Es ist, als würde man sagen, dass ein Übersetzer „konvertieren wird it is zu it's” beim Übersetzen aus dem Englischen ins Französische. Die Unterscheidung zwischen den beiden ist ein Artefakt des Ausgangssprache.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    17. Dezember 2010 um 20:14 Uhr

  • Mir wurde beigebracht. ++n ist zwar schneller, aber niemals langsamer als n++. Für jede beliebige Hardware.

    – EnabrenTane

    23. Dezember 2010 um 5:41 Uhr

Auf GCC 4.4.3 für x86 mit oder ohne Optimierungen kompilieren sie zu genau demselben Assemblercode und benötigen daher dieselbe Zeit für die Ausführung. Wie Sie in der Assembly sehen können, konvertiert GCC einfach n++ hinein n=n+1optimiert es dann in die Ein-Anweisung add (in -O2).

Der Vorschlag deines Lehrers n++ ist schneller gilt nur für sehr alte, nicht optimierende Compiler, die nicht schlau genug waren, die In-Place-Update-Anweisungen für auszuwählen n = n + 1. Diese Compiler sind in der PC-Welt seit Jahren veraltet, können aber immer noch für seltsame proprietäre eingebettete Plattformen gefunden werden.

C-Code:

int n;

void nplusplus() {
    n++;
}

void nplusone() {
    n = n + 1;
}

Ausgabeassembly (keine Optimierungen):

    .file   "test.c"
    .comm   n,4,4
    .text
.globl nplusplus
    .type   nplusplus, @function
nplusplus:
    pushl   %ebp
    movl    %esp, %ebp
    movl    n, %eax
    addl    $1, %eax
    movl    %eax, n
    popl    %ebp
    ret
    .size   nplusplus, .-nplusplus
.globl nplusone
    .type   nplusone, @function
nplusone:
    pushl   %ebp
    movl    %esp, %ebp
    movl    n, %eax
    addl    $1, %eax
    movl    %eax, n
    popl    %ebp
    ret
    .size   nplusone, .-nplusone
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

Ausgangsassembly (mit -O2-Optimierungen):

    .file   "test.c"
    .text
    .p2align 4,,15
.globl nplusplus
    .type   nplusplus, @function
nplusplus:
    pushl   %ebp
    movl    %esp, %ebp
    addl    $1, n
    popl    %ebp
    ret
    .size   nplusplus, .-nplusplus
    .p2align 4,,15
.globl nplusone
    .type   nplusone, @function
nplusone:
    pushl   %ebp
    movl    %esp, %ebp
    addl    $1, n
    popl    %ebp
    ret
    .size   nplusone, .-nplusone
    .comm   n,4,4
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

Der Compiler optimiert n + 1 ins Nichts.

Meinst du n = n + 1?

Wenn dies der Fall ist, werden sie zu einer identischen Assembly kompiliert. (Angenommen, die Optimierungen sind aktiviert und es handelt sich um Anweisungen, nicht um Ausdrücke.)

Wer sagt, dass es das tut? Ihr Compiler optimiert alles weg, wirklich, was es zu einem strittigen Punkt macht.

Moderne Compiler sollten in der Lage sein, beide Formen als gleichwertig zu erkennen und sie in das Format zu konvertieren, das auf Ihrer Zielplattform am besten funktioniert. Es gibt eine Ausnahme von dieser Regel: Variablenzugriffe, die Seiteneffekte haben. Zum Beispiel, wenn n ein speicherabgebildetes Hardwareregister ist, kann das Lesen und Schreiben darauf mehr bewirken, als nur einen Datenwert zu übertragen (Lesen kann beispielsweise einen Interrupt löschen). Sie würden die verwenden volatile Schlüsselwort, um den Compiler wissen zu lassen, dass er bei der Optimierung des Zugriffs auf vorsichtig sein muss nund in diesem Fall generiert der Compiler möglicherweise einen anderen Code von n++ (Inkrementoperation) und n = n + 1 (Lesen, Hinzufügen und Speichern von Vorgängen). Für normale Variablen sollte der Compiler jedoch beide Formen auf dasselbe optimieren.

  • Diese Antwort ist falsch. volatile ergibt keinen Unterschied zwischen ++n und n=n+1. Beide führen zu einem einzelnen Lese- und einem einzelnen Schreibvorgang, und keiner ist atomar. Wenn Sie Atomarität wollen, volatile kann es nicht liefern; warten auf _Atomic tippt C1x ein.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    20. Dezember 2010 um 17:19 Uhr

  • @R: Ich habe nie behauptet, dass “flüchtig” Atomarität liefert. Der Rest Ihres Kommentars ist im allgemeinen Fall nicht wahr (sparen Sie sich den letzten Satz); Die Details, wie ein Compiler Code optimiert, sind implementierungsspezifisch. Meine Antwort erklärt lediglich, wie sich die Verwendung von „volatile“ auf die Compiler-Optimierung im Allgemeinen auswirken kann.

    – bta

    21. Dezember 2010 um 2:01 Uhr


  • @R..GitHubSTOPHELPINGICE: Der Standard verlangt das nicht volatile Bereitstellung von Atomizität, aber wenn eine Plattform in der Lage ist, einige atomare Operationen an Hardwareregistern durchzuführen, ohne alle diejenigen zu unterstützen, die zur Unterstützung von C11-Atomfunktionen verpflichtet sind, dokumentieren Sie, dass sie einige Konstrukte implementiert, die beteiligt sind volatile in atomarer Form scheint der praktischste Weg zu sein, eine solche Unterstützung bereitzustellen. Weitaus besser als atomare Typen, die alle Operationen in fehlerhafter Weise implementieren müssen und keine in der nützlichen Weise implementieren können, die von Hardware unterstützt wird.

    – Superkatze

    9. November 2021 um 7:36 Uhr

Benutzer-Avatar
Welpe

Es tut nicht wirklich. Der Compiler nimmt Änderungen vor, die für die Zielarchitektur spezifisch sind. Mikrooptimierungen wie diese haben oft zweifelhafte Vorteile, sind aber vor allem die Zeit des Programmierers sicherlich nicht wert.

  • Diese Antwort ist falsch. volatile ergibt keinen Unterschied zwischen ++n und n=n+1. Beide führen zu einem einzelnen Lese- und einem einzelnen Schreibvorgang, und keiner ist atomar. Wenn Sie Atomarität wollen, volatile kann es nicht liefern; warten auf _Atomic tippt C1x ein.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    20. Dezember 2010 um 17:19 Uhr

  • @R: Ich habe nie behauptet, dass “flüchtig” Atomarität liefert. Der Rest Ihres Kommentars ist im allgemeinen Fall nicht wahr (sparen Sie sich den letzten Satz); Die Details, wie ein Compiler Code optimiert, sind implementierungsspezifisch. Meine Antwort erklärt lediglich, wie sich die Verwendung von „volatile“ auf die Compiler-Optimierung im Allgemeinen auswirken kann.

    – bta

    21. Dezember 2010 um 2:01 Uhr


  • @R..GitHubSTOPHELPINGICE: Der Standard verlangt das nicht volatile Bereitstellung von Atomizität, aber wenn eine Plattform in der Lage ist, einige atomare Operationen an Hardwareregistern durchzuführen, ohne alle diejenigen zu unterstützen, die zur Unterstützung von C11-Atomfunktionen verpflichtet sind, dokumentieren Sie, dass sie einige Konstrukte implementiert, die beteiligt sind volatile in atomarer Form scheint der praktischste Weg zu sein, eine solche Unterstützung bereitzustellen. Weitaus besser als atomare Typen, die alle Operationen in fehlerhafter Weise implementieren müssen und keine in der nützlichen Weise implementieren können, die von Hardware unterstützt wird.

    – Superkatze

    9. November 2021 um 7:36 Uhr

Benutzer-Avatar
Weizen

Tatsächlich liegt der Grund darin, dass der Operator für Postfix anders definiert ist als für Präfix. ++n wird “n” erhöhen und einen Verweis auf “n” zurückgeben, während n++ wird “n” erhöhen wird a zurückgeben const Kopie von “n”. Daher der Satz n = n + 1 wird effizienter sein. Aber ich muss den obigen Postern zustimmen. Gute Compiler sollten ein ungenutztes Rückgabeobjekt wegoptimieren.

  • @R und mehr zu dem Punkt, wenn man sich den Verlauf ansieht, wurde er erstaunliche 13 Mal bearbeitet, komplett mit Titeländerung, Tag-Änderungen und Fragenänderungen. Mich niederzuschreiben, ohne den historischen Kontext zu nehmen, ist ziemlich schlecht. Normalerweise wäre ich nicht so hochmütig, aber bedeutet das, dass ich alle meine Antworten durchgehen muss, um sicherzustellen, dass die Frage nicht so weit bearbeitet wurde, dass ich einige Monate später heruntergestimmt werden könnte?

    – Weizen

    20. September 2010 um 4:06 Uhr

  • Entschuldigung, war mir nicht bewusst. Ich würde die -1 entfernen, wenn ich könnte.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    17. Dezember 2010 um 20:16 Uhr

  • @R Warum kannst du die -1 nicht entfernen?

    – Ergwun

    22. Dezember 2010 um 0:49 Uhr

  • Nach einer gewissen Zeit können Sie Ihre Bewertung nur noch ändern, wenn die Antwort bearbeitet wird.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    23. Dezember 2010 um 23:55 Uhr

1381890cookie-checkWarum wird n++ schneller ausgeführt als n=n+1?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy