Datenstruktur für Matrizen



  • Es läuft auf den gleichen Maschinencode heraus, nur dass du in deinem Fall mehrere Klassen per Hand pflegen musst und mit dem Template nur eine Stelle hast, wo du Änderungen machen musst.



  • rapso schrieb:

    (bzw vielleicht 10 falls der anfaenger-template-gott zuschlaegt)

    Meinst du da was bestimmtest, oder soll das ne generelle Aussage sein. Man liest das dauernd, aber immer nicht, was den Code der Templates so aufblähen soll
    (Ich weis is Off Topic, aber die Matrix Sache scheint ja geklärt zu sein^^



  • templates können den code aufblähen und vor allem anfänger tun sich oft schwer die gründe dafür auszumachen und zu eliminieren. ich denk das hat er gemeint.


  • Mod

    Pellaeon schrieb:

    rapso schrieb:

    (bzw vielleicht 10 falls der anfaenger-template-gott zuschlaegt)

    Meinst du da was bestimmtest, oder soll das ne generelle Aussage sein. Man liest das dauernd, aber immer nicht, was den Code der Templates so aufblähen soll
    (Ich weis is Off Topic, aber die Matrix Sache scheint ja geklärt zu sein^^

    geht um das resultierende binary. das liegt daran das alles unnuetzerweise immer geinlined wird. wenn du also etwas aufwendiges machst wie z.b. die inverse einer matrix44 (oder selbst ne multiplikation davon), dann wird das ueberall direkt in den code eingebracht, das macht die binary sehr viel groesser als noetig und kann am ende langsammere programme bedeuten. manche nutzen templates auch explizit um z.b. loop unrolling zu machen weil sie das mal irgendwo mitbekommen haben wie das geht, dann wird auch oft viel unperformanter code generiert z.b. wird die branch prediction eventuell viel effizienter genutzt falls man eine bedingung drinnen hat ohne unrolling.

    das tykische ist ja leider das viele meinen durch templates irgendwas schneller/besser gemacht zu haben obwohl es richtig gebenchmarkt dann das gegenteil ist. doch leider ist das oft etwas was ohne benchmarks am anfang vorweg genommen wird und als tatsache eklaert wird. normalerweise lernt jeder juniorprogrammer dass erst der code stehen muss und dann die optimierungen kommen, doch hinsichtlich templates scheint diese philosophie oft zu versagen.



  • rapso schrieb:

    das liegt daran das alles unnuetzerweise immer geinlined wird.

    Das ist schlichtweg falsch. Hier wird die "add"-Methode nicht geinlined (zumindest nicht zwangweise):

    #include <iostream>
    
    template<typename T> class Adder
    {
    public:
    	Adder(const T& v1, const T& v2)
    		: v1(v1), v2(v2)
    	{
    	}
    
    	T add() const;
    
    private:
    	T v1, v2;
    };
    
    template<typename T> T Adder<T>::add() const
    {
    	return v1 + v2;
    }
    
    int main()
    {
    	float v1, v2;
    	std::cin >> v1 >> v2;
    	Adder<float> adder(v1, v2);
    	std::cout << adder.add();
    	return 0;
    }
    


  • TomasRiker schrieb:

    Vertexwahn schrieb:

    http://loop.servehttp.com/~vertexwahn/public_html_an_turing/MatrixHtml/_matrix_8h-source.html

    Benutzt du die auch für 4x4-Transformationsmatrizen?

    Nein - für 4x4 Matrizen habe ich eine spezielle Implementierung


  • Mod

    TomasRiker schrieb:

    rapso schrieb:

    das liegt daran das alles unnuetzerweise immer geinlined wird.

    Das ist schlichtweg falsch. Hier wird die "add"-Methode nicht geinlined (zumindest nicht zwangweise):

    #include <iostream>
    
    template<typename T> class Adder
    {
    public:
    	Adder(const T& v1, const T& v2)
    		: v1(v1), v2(v2)
    	{
    	}
    
    	T add() const;
    
    private:
    	T v1, v2;
    };
    
    template<typename T> T Adder<T>::add() const
    {
    	return v1 + v2;
    }
    
    int main()
    {
    	float v1, v2;
    	std::cin >> v1 >> v2;
    	Adder<float> adder(v1, v2);
    	std::cout << adder.add();
    	return 0;
    }
    

    😮
    ich nehm das einfach mal als sarkasmus hin.



  • rapso schrieb:

    😮
    ich nehm das einfach mal als sarkasmus hin.

    Was soll denn dieser Kommentar? Natürlich ist der Code sinnlos. Er soll dir nur zeigen, dass Template-Methoden nicht zwangsweise geinlined werden, wie du vorhin behauptet hast.
    Wenn du mir nicht glaubst - nimm den Code, kompilier ihn und schau dir den Assembler-Code an. "add" wird durch ein "call" aufgerufen, also wird nicht geinlined.
    Wenn du die Methoden direkt in der Klasse deklarierst, ist das natürlich was Anderes - genau wie bei Nicht-Template-Klassen.


  • Mod

    00401022  fld         dword ptr [v2] 
    00401025  fadd        dword ptr [v1] 
    00401028  add         esp,8 
    0040102B  fstp        qword ptr [esp]
    

    btw. default projekt im releasebuild mit VC++



  • Klar, so kleine Methoden inlined er bereitwillig (würde er auch bei einer Nicht-Template-Klasse tun, hab ich grade getestet).
    Nimm mal:

    Inline function expansion:
    Only __inline (/Ob1)


  • Mod

    und du nimm mal die optionen die man bei einem spiel normalerweise verwendet (vermutlich inline all suitable, 03 usw.) und zeig mir ein beispiel das in der praxis trotz template nicht inlined wird in deiner matrix.



  • Inline all suitable, Full Optimization

    template<typename T> T Adder<T>::aBitMoreComplicatedMethod() const
    {
    	T x = v1;
    	for(int i = 0; i < 16; i++)
    	{
    		x += (v2 - x) * v1;
    	}
    
    	return x;
    }
    

    wird nicht geinlined,

    template<typename T> T Adder<T>::add() const
    {
    	return v1 + v2;
    }
    

    hingegen schon.
    Ist zwar jetzt nicht aus meinem Matrix-Template, aber diese Funktion ist immer noch simpler als z.B. eine Invertierung.

    Warum sollte der Compiler auch einen Unterschied machen?
    Ein Template ist doch einfach nur eine Vorlage. Daraus erzeugt er eine Klasse und die kompiliert er so wie eine normale Klasse auch.


  • Mod

    TomasRiker schrieb:

    Inline all suitable, Full Optimization
    ...
    wird nicht geinlined,

    dann machst du etwas bei deinen settings falsch

    int main()
    {
    00401930  sub         esp,8 
    00401933  push        esi  
    	float v1, v2;
    	scanf("a %f\n",&v1);
    00401934  mov         esi,dword ptr [__imp__scanf (4020B0h)] 
    0040193A  lea         eax,[esp+4] 
    0040193E  push        eax  
    0040193F  push        offset type_info::`vftable'+2Ch (40212Ch) 
    00401944  call        esi  
    	scanf("b %f\n",&v2);
    00401946  lea         ecx,[esp+10h] 
    0040194A  push        ecx  
    0040194B  push        offset type_info::`vftable'+34h (402134h) 
    00401950  call        esi  
    	Adder<float> adder(v1, v2);
     	printf("%f\n",adder.add());
    00401952  movss       xmm0,dword ptr [esp+14h] 
    00401958  movss       xmm1,dword ptr [esp+18h] 
    0040195E  add         esp,10h 
    00401961  movaps      xmm3,xmm0 
    00401964  mov         eax,2 
    00401969  pop         esi  
    0040196A  lea         ebx,[ebx] 
    00401970  sub         eax,1 
    00401973  movaps      xmm2,xmm1 
    00401976  subss       xmm2,xmm3 
    0040197A  mulss       xmm2,xmm0 
    0040197E  addss       xmm2,xmm3 
    00401982  movaps      xmm3,xmm1 
    00401985  subss       xmm3,xmm2 
    00401989  mulss       xmm3,xmm0 
    0040198D  addss       xmm3,xmm2 
    00401991  movaps      xmm2,xmm1 
    00401994  subss       xmm2,xmm3 
    00401998  mulss       xmm2,xmm0 
    0040199C  addss       xmm2,xmm3 
    004019A0  movaps      xmm3,xmm1 
    004019A3  subss       xmm3,xmm2 
    004019A7  mulss       xmm3,xmm0 
    004019AB  addss       xmm3,xmm2 
    004019AF  movaps      xmm2,xmm1 
    004019B2  subss       xmm2,xmm3 
    004019B6  mulss       xmm2,xmm0 
    004019BA  addss       xmm2,xmm3 
    004019BE  movaps      xmm3,xmm1 
    004019C1  subss       xmm3,xmm2 
    004019C5  mulss       xmm3,xmm0 
    004019C9  addss       xmm3,xmm2 
    004019CD  movaps      xmm2,xmm1 
    004019D0  subss       xmm2,xmm3 
    004019D4  mulss       xmm2,xmm0 
    004019D8  addss       xmm2,xmm3 
    004019DC  movaps      xmm3,xmm1 
    004019DF  subss       xmm3,xmm2 
    004019E3  mulss       xmm3,xmm0 
    004019E7  addss       xmm3,xmm2 
    004019EB  jne         main+40h (401970h) 
    004019ED  sub         esp,8 
    004019F0  cvtss2sd    xmm0,xmm3 
    004019F4  movsd       mmword ptr [esp],xmm0 
    004019F9  push        offset type_info::`vftable'+3Ch (40213Ch) 
    004019FE  call        dword ptr [__imp__printf (4020A8h)] 
    	return 0;
    00401A04  xor         eax,eax 
    }
    
    template<typename T> T Adder<T>::add() const
    {
    	return v1 + v2;
    }
    

    Warum sollte der Compiler auch einen Unterschied machen?

    tja, wieso macht er das? steck doch mal deine templates in ein header in nutze sie testweise paar mal... geht... und mach das gleiche nun ohne templates... ploetzlich gibt es mehrere definitionen der selben funktion und es geht nicht mehr... templates werden lokal in jedes .o (oder .bin bei vc++) neu erstellt, das kannst du nicht verhindern, der code wird zwangsweise groesser und bei normalen optimierungsoptionen wird das auch immer in-place gemacht.

    Ein Template ist doch einfach nur eine Vorlage. Daraus erzeugt er eine Klasse und die kompiliert er so wie eine normale Klasse auch.

    nicht ganz, ein template ist quasi annonym (der linker muss nichts von dessen existenz wissen) und wird von vielen compilern einfach in den code gepatcht. selbst ewig lange funktionen stopft dir der compiler in-place rein und wenn du die 100 mal machst, dann packt er dir die auch so oft rein und manche compiler hoeren dann nie auf zu compilieren (woertlich) statt einfach nen call zu machen was in jeglicher hinsicht besser waere.



  • ... und nun ersetz mal die Template-Klasse durch eine normale Klasse, wo float statt T drinsteht. Es kommt derselbe Code raus. Ebenfalls alles inline reingeklatscht.

    Mit SSE inlined er, ohne SSE nicht.

    Also zeig du mir doch bitte mal ein Beispiel, wo er die Methode einer Template-Klasse anders inlined als die Methode der äquivalenten normalen Klasse.



  • rapso schrieb:

    geht um das resultierende binary. das liegt daran das alles unnuetzerweise immer geinlined wird. wenn du also etwas aufwendiges machst wie z.b. die inverse einer matrix44 (oder selbst ne multiplikation davon), dann wird das ueberall direkt in den code eingebracht, das macht die binary sehr viel groesser als noetig und kann am ende langsammere programme bedeuten. manche nutzen templates auch explizit um z.b. loop unrolling zu machen weil sie das mal irgendwo mitbekommen haben wie das geht, dann wird auch oft viel unperformanter code generiert z.b. wird die branch prediction eventuell viel effizienter genutzt falls man eine bedingung drinnen hat ohne unrolling.

    das tykische ist ja leider das viele meinen durch templates irgendwas schneller/besser gemacht zu haben obwohl es richtig gebenchmarkt dann das gegenteil ist. doch leider ist das oft etwas was ohne benchmarks am anfang vorweg genommen wird und als tatsache eklaert wird. normalerweise lernt jeder juniorprogrammer dass erst der code stehen muss und dann die optimierungen kommen, doch hinsichtlich templates scheint diese philosophie oft zu versagen.

    Und wie soll man das umgehen? Weil du schreibst ja oben was von "Anfänger-Gott ...".
    Ist doch erstmal das Prinzip von Templates, dass in jeder o Datei eine entsprechende "Template-Instanz" erzeugt wird. Wie soll man das verhindern?
    Ok inline kann man weglassen, in eurem Disput scheint das ja ne Compiler-Optimierung zu sein, dass er ohne inline Schlüsselwort trotzdem inlined.
    Aber im Allgemeinen?


  • Mod

    TomasRiker schrieb:

    ... und nun ersetz mal die Template-Klasse durch eine normale Klasse, wo float statt T drinsteht. Es kommt derselbe Code raus. Ebenfalls alles inline reingeklatscht.

    ja genau das war mein beispiel im letzten absatz. wie gesagt, kann er das in zwei implementierungsdateien dann nicht mehr verwenden weil normale implementierungen nicht mehr annonym sind, es compiliert also nicht mal.

    Mit SSE inlined er, ohne SSE nicht.

    wie gesagt, mit den optimierungen die bei jedem spiel dabei sind.

    Also zeig du mir doch bitte mal ein Beispiel, wo er die Methode einer Template-Klasse anders inlined als die Methode der äquivalenten normalen Klasse.

    s.o.


  • Mod

    Pellaeon schrieb:

    Und wie soll man das umgehen? ...

    indem man erst optimiert wenn der code steht und stabil ist.



  • @rapso:
    Es ging doch darum: Du hast gesagt, Methoden von Template-Klassen würden immer inline aufgerufen. Soll dein Beispiel das jetzt widerlegen, oder wie?
    Ich habe testweise die Adder<T> und eine FloatAdder (normale Klasse mit float) genommen. Egal, welche Einstellungen ich nehme: Beide werden gleich geinlined. Wenn er eine Methode bei Adder<T> inlined, dann tut er es auch bei FloatAdder und umgekehrt.


  • Mod

    TomasRiker schrieb:

    @rapso:
    Es ging doch darum: Du hast gesagt, Methoden von Template-Klassen würden immer inline aufgerufen. Soll dein Beispiel das jetzt widerlegen, oder wie?

    das war der beweis um deine behauptung zu wiederlegen dass templateklassen genau wie normale klassen behandelt werden.

    Ich habe testweise die Adder<T> und eine FloatAdder (normale Klasse mit float) genommen. Egal, welche Einstellungen ich nehme: Beide werden gleich geinlined. Wenn er eine Methode bei Adder<T> inlined, dann tut er es auch bei FloatAdder und umgekehrt.

    ja, du hast vollkommen recht.



  • Dein Argument, dass die Template-Klassen pro Objektdatei instanziert werden, ist natürlich auch korrekt.
    Das macht die Objektdateien größer.

    Angenommen wir haben zwei CPP-Dateien A.cpp und B.cpp, die beide jeweils die Template-Klasse Adder<float> benutzen. In A.obj und B.obj ist nach dem Kompilieren jeweils der ganze Code von Adder<float> drin. Natürlich, das ist nicht sonderlich schön.

    Wenn jetzt der Linker kommt und A.obj und B.obj zu einer EXE zusammenlinkt, dann wird er aber doch nur die erste Instanzierung der Klasse nehmen und die andere verwerfen - oder nicht??


Anmelden zum Antworten