Einfache Datentypen: Pass by reference/value



  • Hallo,
    ich habe es mir irgendwie angewöhnt, mehr oder weniger alle Parameter eigentlich immer als const-Reference zu übergeben (wenn nicht anders benötigt).
    Nun gewöhne ich mich langsam an C++17 und habe mich ein wenig mit std::string_view beschäftigt, was man ja einer Übergabe als const std::string& vorziehen sollte, um die unnötige Erstellung eines std::strings zu vermeiden bei Übergabe eines String-Literals.
    Meine Frage ist nun, ob es eigentlich überhaupt sinnvoll ist, kleinere Typen wie z.B. bool und std::size_t als Referenz zu übergeben oder ob das nicht sogar egal ist (Optimierung) oder sogar langsamer (da ein 64-bit Zeiger kopiert wird).
    Und was haltet ihr von einer std::string_view-Referenz? Sinnvoll oder sinnlos?

    Danke!



  • Bei allem was kleiner als (oder gleich gross wie) ein Register ist, ist Übergabe "by const reference" meist ein Fehler. Weil im besten Fall gleich schnell und im schlechtesten Fall langsamer. Gründe: Bei Übergabe "by reference"...

    • ...muss der Wert oft "unnötigerweise" in den Speicher geschrieben werden, damit es überhaupt eine Adresse gibt.
    • ...muss der Wert oft nach dem Aufruf neu aus dem Speicher geladen werden, da die aufgerufene Funktion den Wert geändert haben könnte. (Daran ändert IIRC leider auch const oft nichts, da const_cast in vielen Fällen völlig legal ist und Änderungen mittsels const_cast oft definiertes Verhalten haben.)
    • ...hast du eine "Indirection" mehr als wenn direkt der Wert übergeben wird.

    Je nachdem die die aufgerufene Funktion aussieht und ob sie "inlined" wird oder nicht, können oben genannte Dinge eine Rolle spielen oder auch nicht. (Bei Funktionen die nichts ändern und sich den Wert einfach nur angucken, wird es oft keinen Unterschied machen, so lange die Funktion "inlined" wird.)


    std::string_view würde ich persönlich eher "by value" übergeben. Weil klein genug (bloss 2 Variablen mit Zeigergrösse) und einfacher Kopierkonstruktor (=default).



  • @unterfliege
    Auf den Optimierer würde ich mich da nicht verlassen. Wenn's blöd läuft, muß der Compiler für einen einfachen Datenwert erst ein temporäres Objekt erzeugen und dann dessen Adresse übergeben. Nach Rücksprung aus der Funktion muß das temporäre Objekt wieder entfernt werden. Klingt nicht so effizient.

    VG



  • Danke.
    Dann werde ich zukünftig bei allem <= 64bit auf die const-Referenz verzichten.



  • So lange der Kopierkonstruktor und Destruktor billig sind.
    Ein shared_ptr ist z.B. auch klein (zwei Zeiger, von der Grösse her gleich wie eine std::string_view), aber nicht ganz billig zu kopieren (Kopieren + Zerstören meist so grob Grössenordnung 30~60 Zyklen).


  • Mod

    @hustbaer Ich wuerde sogar behaupten, alles was unter zwei bis vier Registern ist. Weil auch Klassen z.T. einfach ueber Register aufgeteilt werden, wenn sie der einzige Parameter sind. Zumindest meine ich, dass LLVM das tut.

    Andererseits bin ich mir nicht sicher, ob Performance dadurch so stark beeinflusst wird, da alle Funktionen die im critical path liegen sowieso geinlined werden sollten, wonach bei trivial kopierbaren Klassen kein Unterschied mehr bestehen sollte.



  • @Columbo Ja... das Thema ist recht komplex würde ich sagen 🙂
    Wenns der einzige Parameter ist, dann können natürlich auch 2-4 Register OK sein. In bestimmten Fällen vielleicht sogar noch deutlich mehr.


Log in to reply