Rückgabewertoptimierung und Kopierelision in C

Lesezeit: 5 Minuten

Benutzeravatar von Z boson
Z-Boson

Einige Leute wissen nicht, dass es möglich ist, Strukturen nach Wert in C zu übergeben und zurückzugeben. Meine Frage betrifft den Compiler, der unnötige Kopien erstellt, wenn er Strukturen in C zurückgibt. Verwenden Sie C-Compiler wie GCC Renditeoptimierung (RVO) Optimierung oder ist dies ein reines C++-Konzept? Alles, was ich über RVO und Copy Elision gelesen habe, bezieht sich auf C++.

Betrachten wir ein Beispiel. Ich implementiere gerade eine Double-Double-Datentyp in C (oder eher Float-Float, um damit zu beginnen, weil ich es einfach finde, Komponenten zu testen). Betrachten Sie den folgenden Code.

typedef struct {
    float hi;
    float lo;
} doublefloat;

doublefloat quick_two_sum(float a, float b) {
    float s = a + b;
    float e = b - (s - a);
    return (doublefloat){s, e};
}

Erstellt der Compiler eine temporäre Kopie der doublefloat Wert, den ich zurückgebe, oder kann die temporäre Kopie entfernt werden?

Was ist mit der benannten Rückgabewertoptimierung (NRVO) in C? Ich habe eine andere Funktion

doublefloat df64_add(doublefloat a, doublefloat b) {
    doublefloat s, t;
    s = two_sum(a.hi, b.hi);
    t = two_sum(a.lo, b.lo);
    s.lo += t.hi;
    s = quick_two_sum(s.hi, s.lo);
    s.lo += t.lo;
    s = quick_two_sum(s.hi, s.lo);
    return s;
}

In diesem Fall gebe ich eine benannte Struktur zurück. Kann in diesem Fall auf die temporäre Kopie verzichtet werden?

Es sollte erwähnt werden, dass dies eine allgemeine Frage für C ist und dass die Codebeispiele, die ich hier verwendet habe, nur Beispiele sind (wenn ich dies optimiere, werde ich sowieso SIMD mit Intrinsics verwenden). Mir ist bewusst, dass ich mir die Assembly-Ausgabe ansehen könnte, um zu sehen, was der Compiler tut, aber ich denke, das ist trotzdem eine interessante Frage.

  • @BaummitAugen, ich war mir auch nicht sicher, ob ich das C++-Tag verwenden soll. Aber ich glaube, ich habe in meiner Frage deutlich gemacht, dass es um C geht. Ich hatte gehofft, dass das C++-Tag Leute anziehen würde, die Experten in beiden Sprachen sind.

    – Z-Boson

    4. Mai 2015 um 15:45 Uhr

  • @IvayloStrandjev immer noch die Frage zu C, das Tag gilt für die Frage, nein?

    – BeyelerStudios

    4. Mai 2015 um 15:46 Uhr

  • @IvayloStrandjev Die Frage ist: Haben wir RVO in C? Auch wenn die Antwort “Nein” war, trifft das C-Tag definitiv zu, weil er nach C fragt.

    – Baum mit Augen

    4. Mai 2015 um 15:46 Uhr


  • @jamesqf: Die offensichtliche Antwort würde beinhalten, dass C durch den C-Standard definiert wird, nicht durch Ihr persönliches mentales Modell.

    – Jerry Sarg

    5. Mai 2015 um 1:49 Uhr

  • @jamesqf, die Rückgabe dieser Strukturen nach Wert in meinen Beispielen ist meiner Meinung nach lesbarer und logischer und nicht unbedingt weniger effizient. Früher dachte ich wie du. Das ist der Grund, warum ich diese Frage gestellt habe. Mein mentales Modell von C entwickelt sich weiter. Ich würde jetzt das Gegenargument vorbringen, dass die Verwendung eines Zeigers eine vorzeitige Optimierung und möglicherweise weniger effizient ist (IMHO sollten Sie nur optimieren, was der Compiler nicht kann, nicht was er kann). Darüber denke ich noch nach.

    – Z-Boson

    5. Mai 2015 um 7:36 Uhr


Benutzeravatar von Jerry Coffin
Jerry Sarg

RVO/NRVO sind nach der „Als-ob“-Regel in C eindeutig erlaubt.

In C++ können Sie beobachtbare Nebeneffekte bekommen, weil Sie den Konstruktor, Destruktor und/oder Zuweisungsoperator überladen haben, um diese Nebeneffekte zu erzeugen (z. B. etwas ausgeben, wenn eine dieser Operationen auftritt), aber in C ist das nicht der Fall können diese Operatoren überladen, und die eingebauten haben keine beobachtbaren Nebenwirkungen.

Ohne sie zu überladen, erhalten Sie keine beobachtbaren Nebenwirkungen durch das Entfernen von Kopien, und daher nichts, was einen Compiler daran hindern könnte, dies zu tun.

  • Die Adresse der Variablen in der Funktion und die Adresse der nach außen zugewiesenen Variablen können identisch sein, da sie sich nicht überschneidende Lebensdauern haben: Die Adresse der temporären Brücke kann nicht übernommen werden, und es kann nicht erkannt werden, dass sie die Adresse der anderen beiden Variablen teilt . Dies macht es meiner Meinung nach unmöglich zu erkennen, dass dies (theoretisch) unter dem Standard passiert ist: Wenn die Variable in der Funktion und die Variable, die außerhalb zugewiesen ist, in der Praxis dieselbe Adresse haben, stehen die Chancen gut, dass Sie Zeuge von NRVO werden.

    – Yakk – Adam Nevraumont

    4. Mai 2015 um 15:57 Uhr


  • Beim Testen auf gcc, g++, clang, clang++ stellt sich heraus, dass jeder NRVO richtig macht, außer gcc -xcdas überflüssige Kopien erzeugt, wenn Sie: struct s f() { struct s x = g(); return x; }

    – Spitzenreiter

    17. November 2015 um 6:17 Uhr


  • @ Yakk-Adam Nevraumont Die Adresse der Variablen in der Funktion und die Adresse der nach außen zugewiesenen Variablen können identisch sein, da sie sich nicht überschneidende Lebensdauern haben Tun s und ret haben sich nicht überschneidende Lebensdauern oder Clang vermasselt und NRVO sichtbar gemacht? godbolt.org/z/Ef4sxseTj

    – Sprachanwalt

    1. Juli um 11:05 Uhr


  • Ich denke, dies kann LLVM-Semantik sein, die C ++ nachahmt, und kann sich manchmal für C-Programme schlecht verhalten. Aber niemand kümmert sich darum.

    – Sprachanwalt

    1. Juli um 13:30 Uhr


  • @LanguageLawyer Ich kenne nicht genug C, um diese Frage zu beantworten. Elision ist real in C++; Ich weiß, dass einige Auslassungen in C “als ob” erlaubt sein können, ich weiß nicht, wie weit es in C gehen kann. Ich müsste verstehen, was Adressen in C (im Gegensatz zu C++) auf einer viel tieferen Ebene bedeuten als ich jetzt.

    – Yakk – Adam Nevraumont

    1. Juli um 15:03 Uhr


Der Grund, warum es für C++ so viel behandelt wird, liegt darin, dass RVO in C++ Nebenwirkungen hat (dh weder den Destruktor der temporären Objekte noch den Kopierkonstruktor oder den Zuweisungsoperator der resultierenden Objekte aufruft).

In C gibt es keine möglichen Nebenwirkungen, nur potenzielle Leistungsverbesserungen. Ich sehe keinen Grund, warum eine solche Optimierung nicht von einem Compiler durchgeführt werden könnte. Zumindest verbietet es nichts im Standard.

Wie auch immer, die Optimierung ist vom Compiler und der Optimierungsebene abhängig, daher würde ich nicht darauf wetten, wenn es um kritische Codepfade geht, es sei denn, der verwendete Compiler ist gut definiert und es wird nicht erwartet, dass er sich ändert (was immer noch häufig der Fall ist).

1416170cookie-checkRückgabewertoptimierung und Kopierelision in C

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

Privacy policy