Seite 1 von 2

SSE

Verfasst: 12. Jan 2012 18:45
von Thomas Huxhorn
Sagmal, hat jemand ne Idee, warum mein Programm um 10% LANGSAMER wird, wenn ich z.b. eine einfache vec3 Multiplikation in SSE einbaue?
Das war um 2. Praktikum aber nicht so :(
Es rechnet noch richtig.

Re: SSE

Verfasst: 12. Jan 2012 19:34
von Ankou
wenn du eine einzelne Vektormultiplikation mittels SSE umsetzt ist der overhead für den asm Block vermutlich größer als der Gewinn.

Re: SSE

Verfasst: 12. Jan 2012 21:10
von Thomas Huxhorn
Hm, ja stimmt. Blöd.

Re: SSE

Verfasst: 13. Jan 2012 09:53
von mw1039
Du kannst mal versuchen dir von gcc automatisch SSE-Code generieren zu lassen. Tipp mal ein "man gcc", dann "/-msse" und dann mit "n" weitersuchen, bis du das richtige gefunden hast (ungefaehr bei Zeile 6429). Mit "q" kommst du aus der manpage wieder raus.
Du kannst gcc (z.B. ueber die CMakeLists.txt) beispielsweise die Schalter "-msse", "-msse2", "-msse3", "-msse4" oder "-mfpmath=sse" uebergeben. Keine Ahnung wie gut das klappt, aber einen Versuch waere es wert.

Re: SSE

Verfasst: 13. Jan 2012 11:59
von Thomas Huxhorn
Danke, aber habe ich schon alles versucht. Bringt sogut wie garkein Unterschied in der Laufzeit.

Re: SSE

Verfasst: 13. Jan 2012 15:55
von Ronny
Ein asm-Block an sich erzeugt keinen Overhead. Wie hast du denn den Vektor in ein SSE Register geladen?
Am besten sind leider nur float Vektoren mit vier Komponenten fuer SSE geeignet die auch noch aligned sind um sie mit movaps zu laden.
Es lohnt sich dazu sogar, einen dreidimensionalen Vektor um eine vierte Dummy-Komponente zu erweitern.

Re: SSE

Verfasst: 13. Jan 2012 15:59
von Thomas Huxhorn
Hier ein Beispiel. Habe die vec3 Klasse natürlich um eine 4. float Zahl erweitert.

Code: Alles auswählen

                "movups (%[src]), %%xmm1\n"
                :
                : [src] "a" (&a.x),
                  [dest] "b" (&result.x)
                : "%esi"
                );

Re: SSE

Verfasst: 13. Jan 2012 17:07
von Ronny
Wenn deine Vektor Klasse nicht 16 Byte aligned ist, resultiert die movups Anweisung in mehreren Speicherzugriffen.
Und wenn du nach dem Laden dann auch nur eine einzige Multiplikation ausfuehrst, ist fragwuerdig ob sich der Aufwand lohnt.

Re: SSE

Verfasst: 13. Jan 2012 17:48
von x539
gcc verwendet sse schon von alleine wenn man mit -O3 compiliert. Deshalb wird es schwer das per handgeschriebenen inline-Asm zu übertreffen. Im 2. praktikum war standard mäßig die Optimierung aus geschaltet, desshalb war der unterschied deutlicher zu sehen.

Außerdem kann der Compiler, den Code nicht mehr so gut Optimieren wenn du inline-Asm blöck reinschreibst. Zum Beispiel kann er die reinfolge der Befehle nicht mehr optimal den die pipline der CPU anpassen, weil er den inline-asm-block fest übernehemen muss. Schlecht ist außerdem wenn du feste Register vorgibst die du verwenden willst. Dann müssen eventuell werte sinnlos hin und hergeschoben werden, nur weil du dich auf rax, rbx, etc. festgelegt hast obwohl das genauso gut ein beliebiges anders Register hätte sein können, in dem der wert bereits steht, bzw. in dem der Wert später benötigt wird.

Re: SSE

Verfasst: 13. Jan 2012 18:11
von Thomas Huxhorn
Hm wenn ich einfach vec() aufrufe, ja dann kann das sonstwo im Speicher liegen. Kenne nur die Möglichkeit mit malloc Speicher auszurichten.

Also mit -O3 war mein Programm sogar langsamer. Das erklärt es wohl.

Re: SSE

Verfasst: 13. Jan 2012 20:15
von simon.r
x539 hat geschrieben:gcc verwendet sse schon von alleine wenn man mit -O3 compiliert. Deshalb wird es schwer das per handgeschriebenen inline-Asm zu übertreffen. Im 2. praktikum war standard mäßig die Optimierung aus geschaltet, desshalb war der unterschied deutlicher zu sehen.

Außerdem kann der Compiler, den Code nicht mehr so gut Optimieren wenn du inline-Asm blöck reinschreibst. Zum Beispiel kann er die reinfolge der Befehle nicht mehr optimal den die pipline der CPU anpassen, weil er den inline-asm-block fest übernehemen muss. Schlecht ist außerdem wenn du feste Register vorgibst die du verwenden willst. Dann müssen eventuell werte sinnlos hin und hergeschoben werden, nur weil du dich auf rax, rbx, etc. festgelegt hast obwohl das genauso gut ein beliebiges anders Register hätte sein können, in dem der wert bereits steht, bzw. in dem der Wert später benötigt wird.
Ganz so stimmt das glaube ich nicht. Gibt man als Parameter "-mfpmath=sse" und "-msse2" an, dann verwendet gcc anstatt der FPU die xmm Register für skalare Operationen. Auf die Art und Weise lassen sich Berechnungen besser optimieren, da die Stack Struktur der FPU dafür eher ungeeignet ist. Kompiliert man auf 64bit (wie etwa auf dem Cluster), werden diese Flags automatisch gesetzt. Für automatische Vektorisierung (ps-Befehle) braucht man wahrscheinlich eher den Intel Compiler... Inwiefern gcc die Befehle konkret auf die Pipeline vom verbauten Prozessor optimieren kann weiß ich nicht, schließlich unterstüzt die g++Verison 4.5 auf dem Cluster als Architekturprofile maximal core2 und nicht corei7.^^

Re: SSE

Verfasst: 13. Jan 2012 20:38
von x539
simon.r hat geschrieben:Inwiefern gcc die Befehle konkret auf die Pipeline vom verbauten Prozessor optimieren kann weiß ich nicht, schließlich unterstüzt die g++Verison 4.5 auf dem Cluster als Architekturprofile maximal core2 und nicht corei7.^^
Ich denke nicht das die sich starke unterscheiden, aber
mir ging es auch eher um allgemeine Optimierung:
z.B. ist

Code: Alles auswählen

movl foo, rax
xor  foo, rax
movl foo, rbx
xor foo, rbx
langsamer als,

Code: Alles auswählen

movl foo, rax
movl foo, rbx
xor foo, rax
xor foo, rbx
weil die beiden mov befehle gleichzeitig ausführt werden können.
wenn jetzt

Code: Alles auswählen

movl foo, rax
xor foo, rax
ein inline-block ist fällt diese optimierung weg.


Hmm… das gcc SSE nur für skalare operationen verwendet ist mir noch gar nicht aufgefallen. Ich hatte erwartet, dass zumindest die vec3d Funktionen nicht skalar umgesetzt werden.

Re: SSE

Verfasst: 16. Jan 2012 00:37
von Jörg B
Auch ich wollte auch etwas mit SSE rumspielen und habe mich an der Normalisierung versucht, da die Methode schon recht viel Zeit frisst.

Meine Variante besteht meine einzelnen Tests immer und ist dort sogar schneller als die gegebene C++ Variante, jedoch wird sie beim gesamten Rendern immer langsamer und nach ca. 500 Aufrufen geht sie so gut wie gar nicht mehr.

Jetzt wo die Abgabe vorbei ist, kann mir ja vielleicht jemand einen Tipp geben wieso das so ist? Irgendwo muss ich ja irgendwas zu viel erzeugen was dann Resourcen blockiert.

Grüße Jörg


vec.cpp

Code: Alles auswählen

vec3 vec3::normalize() const
{
	vec3 result = *this;

	 asm (  
      "\n\tmovups (%1), %%xmm0"
      "\n\tmovups %%xmm0, %%xmm2"	  
      "\n\tmulps %%xmm0, %%xmm0"  
      "\n\tmovups %%xmm0, %%xmm1"  
	  
      "\n\tshufps $0b10010011,%%xmm0, %%xmm0"  
      "\n\taddps %%xmm0, %%xmm1"  
	  
      "\n\tmovaps %%xmm1, %%xmm0"  
      "\n\tshufps $0b01001110,%%xmm1, %%xmm1"

	  
      "\n\taddps %%xmm1, %%xmm0"  
      "\n\trsqrtps %%xmm0, %%xmm0"  	  
	  
      "\n\tmulps %%xmm2, %%xmm0"
      "\n\tmovups %%xmm0, %0"
	  	 	  
      : "=m"(result)
      : "r"(this) : "xmm0", "xmm1", "xmm2");
	
	/*
	float invL = 1.0f/length();
	result.x *= invL;
	result.y *= invL;
	result.z *= invL; */

	return result;
}

Re: SSE

Verfasst: 18. Jan 2012 18:22
von x539
Jörg B hat geschrieben:Jetzt wo die Abgabe vorbei ist, kann mir ja vielleicht jemand einen Tipp geben wieso das so ist?
Hast du deine Implementierung mal getestet? Die sieht nicht aus als würde sie den Vektor auf Länge 1 normieren sondern nochmal um die Länge strecken.
Btw. Können die GRIS-Rechner kein SSE4.1, dann gäbe es da nämlich noch dpps für das Skalar(/Dot)-Produkt

Re: SSE

Verfasst: 18. Jan 2012 20:47
von simon.r
Ich habe nochmal recherchiert, es gibt doch einen automatischen Vektorisierer, der bereits bei "-O3" eingesetzt wird, allerdings nur auf Schleifne operiert und dazu noch recht wählerisch zu sein scheint. Siehe http://gcc.gnu.org/projects/tree-ssa/vectorization.html, gibt man "-ftree-vectorizer-verbose=2" beim Kompilieren an, bekommt man mehr oder weniger detailliert aufgezählt, welche Schleifen vektorisiert wurden und welche nicht.
Die Cluster Rechner können auf jeden Fall SSE 4.1, wir haben sogar als einzige SSE Instruktion dpps expliziet verwendet - leider ohne wirkliche Geschwindigkeitsänderungen...