Referenzen



  • @hustbaer sagte in Referenzen:

    Komische Definition von "Objekt". In C++ ist selbst ein char ein Objekt. Ein pair<int, int> auch. Oder ne string_view.

    Ich war mir unsicher ob man Builtin-Typen auch als Objekt bezeichnen darf. Dürfte wohl klar sein was gemeint ist.



  • @Luks-Nuke Nein, ist nicht klar. Ist std::string_view für dich ein Objekt? Wenn ja, dann: bitte, bitte, bitte nicht per Referenz übergeben.



  • @hustbaer ich unterscheide zwischen Builtins und Objekten. Wenn ein Klassenobjekt kann natürlich auch so groß sein wie ein Pointer, beispielsweise wenn sie nur einen Pointer als Member hält. Generell sind sie aber mehr als das. Gibt natürlich Ausnahmen, auch Iteratoren. Habe ich wohl zu sehr verallgemeinert.


  • Mod

    @Luks-Nuke sagte in Referenzen:

    @hustbaer ich unterscheide zwischen Builtins und Objekten.

    Solltest du nicht so machen, sonst verstehen dich andere Leute falsch. Hier wäre besser von primitiven ("fundamental") und zusammengesetzten ("compound") Datentypen zu reden. So trifft man dann auch Arrays, die ja auch zusammengesetzt sind aus vielen Teilen, obwohl nirgends eine Klasse vorkommt.

    Als Objekttypen versteht man in C++ alles, was keine Referenz oder Funktion ist.



  • Ok.



  • @Luks-Nuke
    Wenn du "class oder struct" meinst, dann wäre die passende Formulierung die man häufiger liest "object of class type". Das schliesst "struct" mit ein, "class" und "struct" sind ja quasi austauschbar (es ist z.B. eine forward-declaration mit "class" zu machen und die Definition dann mit "struct" - oder umgekehrt).

    @SeppJ
    Laut C++ Standard ist alles was kein "fundamental type" ist, ein "compound type". Und "fundamental type" sind nur:

    • Arithmetische Typen
    • void
    • nullptr_t

    D.h. Zeiger, Referenzen und Enums (inklusive std::byte) sind alles "compound types".

    Das ist ne schön einfache und klare Definition. Aber IMO auch eine die bei vielen für Überraschung sorgen wird.



  • @Luks-Nuke sagte in Referenzen:

    @hustbaer ich unterscheide zwischen Builtins und Objekten.

    Wie klassifizierst du dann Zeiger, Referenzen, Arrays und Enums? Und macht es bei den ersten drei für dich einen Unterschied ob der "underlying type" auch ein "builtin" ist oder z.B. ne struct?



  • @hustbaer sagte in Referenzen:

    Wie klassifizierst du dann Zeiger, Referenzen, Arrays und Enums?

    Ich nehme das nicht so streng. Ich nenne sie bei ihrem Namen.



  • Du verstehst nicht was ich mein.
    Ich meine: wenn du wo "builtin" geschrieben hast, sind dann Zeiger, Referenzen, Arrays und Enums mit gemeint?



  • @hustbaer sagte in Referenzen:

    Ich meine: wenn du wo "builtin" geschrieben hast, sind dann Zeiger, Referenzen, Arrays und Enums mit gemeint?

    Wie gesagt ich habe das nie so genau genommen. Wenn ich von Builtins rede meine ich eigentlich die blau eingefärbten Datentypen.



  • @Luks-Nuke sagte in Referenzen:

    Wenn ich von Builtins rede meine ich eigentlich die blau eingefärbten Datentypen.

    Die was? Meine Musiklehrerin hat auch immer in Noten irgendwelche Farben gesehen - das habe ich auch nie verstanden. Ich verbinde höchtens Emotionen mit Datentypen. Meistens Hass, wenn sich der Typ nicht so verhält, wie ich mir das wünsche 😉



  • Dass es sich um fundamentale Typen handelt wurde ja hier mehr ausführlich gemacht. Dass man diese auch als ein Objekt bezeichnen kann war mir tatsächlich noch nicht bewusst.


  • Mod

    @hustbaer sagte in Referenzen:

    @SeppJ
    Laut C++ Standard ist alles was kein "fundamental type" ist, ein "compound type". Und "fundamental type" sind nur:

    • Arithmetische Typen
    • void
    • nullptr_t

    D.h. Zeiger, Referenzen und Enums (inklusive std::byte) sind alles "compound types".

    Ist das nicht auch das, was Luks-Nuke meint? Tja, das kommt eben von ungwöhnlicher Wortwahl.

    Aber ohne weiter Haare zu spalten, geht die Klassifizierung der Entitäten im Standard sowieso an der Referenzeffizienzfrage vorbei. Allerhöchstens noch insofern, als dass es im Standard Klassifikationen bezüglich der Kopierbarkeit gibt, aber Kopierbarkeit an sich sagt ja nichts über Effizienz aus, und der Standard interessiert sich nicht für Effizienz. Von daher ist die ganze Diskussion über Wortwahl sowieso müßig. Die wahre Antwort liegt irgendwo bei a) es muss kopierbar sein, vorzugsweise trivial (hier kann man noch den Standard konsultieren, was das bedeutet); b) die Menge der zu kopierenden Daten sollte N Pointergrößen nicht überschreiten, wobei N irgendeine Zahl im Bereich 1-20 ist und unvorhersehbar von der Anzahl und Art der Zugriffe abhängt; und c) wenn das alles eine einzige Compiliereinheit ist, dann optimiert dir der Compiler das höchtswahrscheinlich sowieso, egal was man schreibt.

    Aber das wurde alles schon gesagt, die Diskussion über die exakte Klassifikation von Sprachelementen, die danach aufgekommen ist, bringt die Antwort nicht weiter, da die Einordnungen nur als ganz grobe Richtschnur mit der Effizienzantwort zu tun haben.



  • @SeppJ sagte in Referenzen:

    … b) die Menge der zu kopierenden Daten sollte N Pointergrößen nicht überschreiten, wobei N irgendeine Zahl im Bereich 1-20 ist und unvorhersehbar von der Anzahl und Art der Zugriffe abhängt;

    Die Datenmenge sollte kleiner als eine Cache Line sein, denn eine Cache Line wird immer gelesen. Bei aktuellen Intel CPUs ist eine Cache Line 64 Bytes groß.


  • Mod

    @john-0 sagte in Referenzen:

    @SeppJ sagte in Referenzen:

    … b) die Menge der zu kopierenden Daten sollte N Pointergrößen nicht überschreiten, wobei N irgendeine Zahl im Bereich 1-20 ist und unvorhersehbar von der Anzahl und Art der Zugriffe abhängt;

    Die Datenmenge sollte kleiner als eine Cache Line sein, denn eine Cache Line wird immer gelesen. Bei aktuellen Intel CPUs ist eine Cache Line 64 Bytes groß.

    Und wieso ist das nicht das Argument dafür, dass es mindestens 64 Bytes sein müssen, bevor man überhaupt über Referenzen nachdenkt?



  • @SeppJ sagte in Referenzen:

    Und wieso ist das nicht das Argument dafür, dass es mindestens 64 Bytes sein müssen, bevor man überhaupt über Referenzen nachdenkt?

    Weil das Aligment und der Kopieraufwand da noch eine Rolle spielt. Wenn man einen integralen Datentyp nutzt, dann liegt ein Wert immer innerhalb einer Cache Line. Bei zwei oder mehr Werten ist das schon nicht mehr garantiert. Je näher man an die 64 Byte Grenze kommt, desto wahrscheinlicher werden Cache Misses für mehr als einen Wert. Das kann man nur umgehen, in dem man die Datenstruktur explizit so alloziert, dass sie innerhalb einer Cache Line liegt. Dafür wurde ja in C++17 aligned_alloc eingeführt.

    Der Kopieraufwand spielt sicherlich auch eine Rolle, aber nur dann wenn man nicht auf Cache Misses warten muss. Der L1 Cache ist noch sehr schnell, aber selbst der L2 Cache ist schon so langsam, dass das Kopieren nicht mehr ins Gewicht fallen sollte.

    Wenn man stattdessen Zeiger verwendet, ist der Zeiger schnell kopiert. Nur muss man sich immer vor Augen halten, dass die eigentliche Datenstruktur noch geladen werden muss, und die liegt wahrscheinlich in einer anderen Cache Line.


  • Mod

    Das ergibt doch überhaupt keinen Sinn. Zugreifen musst du auf die Daten sowieso, daher sind sie doch Funktionsparameter. Daher sind Überlegungen zu Cachemisses (bei zusammenhängenden Daten vom Programmstack? lol) beim Zugriff völlig egal Entweder hast du sie beim Zugriff oder nicht, aber der Cache weiß ja nicht ob du die Daten willst um damit zu arbeiten oder um sie zu kopieren. Die Frage ist nur, ob die Zusatzkosten für ständige indirekte Zugriffe die Einmalkosten für eine lokale Kopie überwiegen, oder nicht.



  • @SeppJ sagte in Referenzen:

    Daher sind Überlegungen zu Cachemisses (bei zusammenhängenden Daten vom Programmstack? lol)

    Wenn man Referenzen übergibt, liegen nur die Referenzen auf dem Stack! Der Rest kann muss aber nicht auf dem Stack liegen.


  • Mod

    @john-0 sagte in Referenzen:

    @SeppJ sagte in Referenzen:

    Daher sind Überlegungen zu Cachemisses (bei zusammenhängenden Daten vom Programmstack? lol)

    Wenn man Referenzen übergibt, liegen nur die Referenzen auf dem Stack! Der Rest kann muss aber nicht auf dem Stack liegen.

    Du willst Heapobjekte per Kopie übergeben? Kann meinetwegen vorkommen. Aber du redest nach wie vor von Cachemisses auf Daten, die sowieso gelesen werden, ob kopiert oder nicht.



  • Ja, das Lesen der Daten sollte wörscht sein, auch das Schreiben auf den Stack. Was evtl. nicht ganz wörscht ist, ist dass man dadurch mehr Stack "heiss" und im Cache hält, wodurch weniger Platz für andere Daten im Cache ist.

    Ich bin mir ziemlich sicher dass das von Fall zu Fall ganz unterschiedlich ausgehen kann was schneller ist - auch bei Objekten die deutlich grösser als eine Stack-Line sind. Also wenn's wirklich auf Performance ankommt und das Objekt grösser als 1-2 Zeiger ist, lieber profilen.


Anmelden zum Antworten