Operator Überladung [ ]



  • @wob sagte in Operator Überladung [ ]:

    Du greifst mit Vector3dVariable[0] auf x zu, mit 1 auf y und mit 2 auf z. Es nutzt aus, dass in deiner Klasse die 3 Variablen direkt hintereinander definiert sind und damit hintereinander im Speicher stehen. Ich bin mir nicht sicher, dass das immer mit jedem Compiler funktionieren muss, da Padding (Platz zwischen den Variablen) ja Implementation-defined ist. Allerdings sind hier alle 3 Variablen vom gleichen Typ, d.h. es sollte kein Padding benötigt werden.

    Floats sind i.d.R. nur 32Bit groß, so dass der Compiler z.B. für eine 64Bit Plattform hier die Option hat für die Geschwindigkeitsoptimierung die Member auf 64Bit Adressen auszurichten, und dann hat man hier Padding. Will man hier einen Zeiger auf die interne Struktur übergeben, muss man ein Array oder einfach ein Feld der Größe drei anlegen, dann ist das garantiert.



  • Danke euch dann hab ichs jetzt kapiert. Zu den einzelnen Fragen(welches Buch usw.) später heute Abend, werde wohl so gegen 20 Uhr Zeit haben.



  • @john-0 sagte in Operator Überladung [ ]:

    Floats sind i.d.R. nur 32Bit groß, so dass der Compiler z.B. für eine 64Bit Plattform hier die Option hat für die Geschwindigkeitsoptimierung die Member auf 64Bit Adressen auszurichten, und dann hat man hier Padding.

    Ok, sehe ich ein. Mal abgesehen davon, dass es erlaubt ist: macht das denn irgendein Compiler so? gcc/clang richten jedenfalls auf x86_64 per Default die float32 an 4er-Grenzen aus, sodass ein sizeof(struct aus 3 floats)==12 ist. Auch ein struct aus 3 chars hat die Größe 3, nicht 3*8.

    Gut, man kann natürlich struct V3d{float x __attribute__ ((aligned (8))), y __attribute__ ((aligned (8))), z __attribute__ ((aligned (8)));}; schreiben. Damit ist dann der Code im Original kaputt 🙂



  • @Th69: Ja das steht so in dem Buch Foundations of Game Engine Development Volume 1 - Mathematics

    @All: Ich hätte eigentlich auch gedacht ein echtes Array im C-Stil wäre besser: Zum einen aus Performancegründen,
    zum Anderen weil die Elemente dann wirklich hintereinander liegen (was wiederum vorteilhaft für die
    Performance sein soll)

    Wie könnte man es denn noch einfacher schreiben wenn man die Klasse beibehalten will? Objekte finde ich schon sinnvoll weil man sie um nützliche Hilfsfunktionen erweitern kann.

    Man könnte auch einfach:

    float Vector3D[3] = {0,0,0};
    

    schreiben 🙂



  • @wob sagte in Operator Überladung [ ]:

    Mal abgesehen davon, dass es erlaubt ist: macht das denn irgendein Compiler so? gcc/clang richten jedenfalls auf x86_64 per Default die float32 an 4er-Grenzen aus, sodass ein sizeof(struct aus 3 floats)==12 ist.

    Dafür dürfte x86/x86_64 die falsche Plattform sein, da sich die sogar auf die 4Bit Plattform 4004 zurückführen lässt, und Binärkompatibel ist Intel mit 8080. Der Assemblercode des 8008 lässt sich für neuere x86 CPUs auch übersetzen. Das ist ein ziemlicher Wildwuchs an Abnormitäten.

    Ein Kandidat für die Ausrichtung an 64Bit Grenzen wäre ein echter 64Bit Prozessor. SPARC, HP-PA, Power, MIPS, sind alles 32/64Bit Plattformen, d.h. die waren einmal 32Bit und sind dann auf 64Bit erweitert worden. ARM ist sogar 26/32/64Bit. IMHO gab es nur Alpha als reine 64Bit Plattform. D.h. auf Alpha hätte es sein können. Ein weitere Möglichkeit sind die SIMD Einheiten der CPUs, weil hier die richtige Ausrichtung eine enorme Rolle spielt.

    Nachtrag:
    Es geht ja nicht nur darum, was aktuell möglich ist, sondern wie Code in Zukunft verarbeitet werden kann. Man sollte sich daher an die Garantien halten, die die ISO Norm gibt, und nicht spekulieren. Denn wenn etwas die Vergangenheit zeigt, dann ist es die Gewissheit, dass die Zukunft Überraschungen bereithält.



  • Die einfachste Variante wäre wohl

    struct Vector3D {
        float comp[3];
    };
    

    Bzw. wenn du willst dass das Ding per Default alle Komponenten auf 0 hat, dann

    struct Vector3D {
        float comp[3] = {};
    };
    

    Bzw. wenn du es fancy willst, dann halt

    struct Vector3D {
        std::array<float, 3> comp = {};
    };
    

    Damit kannst du auch alles das schreiben:

    Vector3D v;
    Vector3D v2 = v; // copy construction geht
    Vector3D v3 = {1, 2, 3};
    v = v2; //  // copy assignment geht
    
    for (size_t i = 0; i < 3; i++) {
        doStuffWith(v.comp[i]);
    }
    
    for (auto& c : v.comp) {
        doStuffWith(c);
    }
    

    Oder du könntest sowas machen:

    class Vector3D {
    public:
        constexpr Vector3D() = default;
        constexpr Vector3D(float x, float y, float z) : comp{x, y, z} {}
    
        constexpr float x() const { return comp[0]; }
        constexpr float y() const { return comp[1]; }
        constexpr float z() const { return comp[2]; }
    
        constexpr void set_x(float value) { comp[0] = value; }
        constexpr void set_y(float value) { comp[1] = value; }
        constexpr void set_z(float value) { comp[2] = value; }
    
        constexpr float get(size_t n) const { return comp[n]; }
        constexpr void set(size_t n, float value) { comp[n] = value; }
    
    private:
        float comp[3] = {};
    };
    

    Der Vorteil davon wäre dass du die Implementierung dann leicht ändern kannst um z.B. SIMD Typen zu verwenden, ohne dass du alle Stellen anpassen musst wo die Vektoren verwendet werden. Das macht aber fast nur Sinn wenn...

    • Du weitere Memberfunktionen dazumachst zum Addieren, Subtraieren, Multiplizieren und
    • Diese auch häufig verwendet werden und
    • Du wirklich maximale Performance brauchst


  • @hustbaer sagte in Operator Überladung [ ]:

    Diese auch häufig verwendet werden und
    Du wirklich maximale Performance brauchst

    Und die wird in einer Spiele-Engine auf alle Fälle gebraucht.



  • @zipehpa sagte in Operator Überladung [ ]:

    @hustbaer sagte in Operator Überladung [ ]:

    Diese auch häufig verwendet werden und
    Du wirklich maximale Performance brauchst

    Und die wird in einer Spiele-Engine auf alle Fälle gebraucht.

    Möglicherweise. Aber nicht unbedingt. Das meiste wird ja von OpenGL/D3D/... erledigt. Das direkte Rumhantieren mit Vektoren & Co. muss nicht unbedingt besonders relevant sein.



  • @hustbaer sagte in Operator Überladung [ ]:

    Das direkte Rumhantieren mit Vektoren & Co. muss nicht unbedingt besonders relevant sein.

    Da ich mich damit noch nicht auskenne, war ich der Meinung, das wäre besonders wichtig. Oder ist das eher wichtig wenn man von Grund auf ne Engine bauen will? Dass es fertige Vektor und Matrix Bibliotheken gibt ist ja bekannt, bleibt die Frage bis zu welchem Grad man das dann braucht.



  • @zipehpa sagte in Operator Überladung [ ]:

    @hustbaer sagte in Operator Überladung [ ]:

    Das direkte Rumhantieren mit Vektoren & Co. muss nicht unbedingt besonders relevant sein.

    Da ich mich damit noch nicht auskenne, war ich der Meinung, das wäre besonders wichtig. Oder ist das eher wichtig wenn man von Grund auf ne Engine bauen will? Dass es fertige Vektor und Matrix Bibliotheken gibt ist ja bekannt, bleibt die Frage bis zu welchem Grad man das dann braucht.

    Ja, eine solide Matrix-/Vektor-Implementierung ist schon wichtig. Selbst wenn die schwere Arbeit auf der GPU stattfindet, so wird diese nützlich sein, um Datenstrukturen und Buffer zu konstruieren, bevor man diese an den Grafikchip sendet. Auch will man damit vielleicht umfangreiche Berechnungen auf der CPU machen - für programmatische Steuerungen von Objekten Im Spiel (KI und sowas), Kollisionsbestimmung oder auch so banale Dinge wie irgendetwas in der Spielewelt "anzuklicken" indem man die 2D-Mauskoordinaten in die 3D-Welt projiziert. Man wird nicht jeden geometrischen Aspekt eines Spiels (wo man diese Klassen dann benötigt) auf dem Grafikchip ausführen können (oder wollen - vieles könnte man theoretisch auf der GPU machen, das wird dann aber schnell überproportional fummelig).

    Worauf @hustbaer aber wahrscheinlich hinaus will ist, dass diese Implemetierung meistens nicht unbedingt super-optimiert mit SIMD und auf HPC-Niveau sein muss - es sei denn du hast vieleicht ein Spiel, wo extrem viele Simulationsberechnungen auf der CPU gmacht werden, welche mit diesen Vektoren arbeiten, so dass eine mit Vektoroperationen beschäftigte CPU tatsächlich ein limitierender Faktor wird. Das ist aber nicht bei jedem Spiele-Typ der Fall.


Anmelden zum Antworten