Hier ein sehr kleiner Ausschnitt aus der Praxis und der Assembercode. Verwendet wird ein Vector basierend auf SSEvalarray.
auto func(const SSEVector<float,3>& UBAR) { const auto U = UBAR[1]/UBAR[0]; const auto V = UBAR[2]/UBAR[0]; // more code which use U and V return U*V; }
func(Vector<float, 3> const&): movss (%rdi), %xmm2 movss 4(%rdi), %xmm0 divss %xmm2, %xmm0 movss 8(%rdi), %xmm1 divss %xmm2, %xmm1 mulss %xmm1, %xmm0
Wir sehen ein paar MOVs und insbesondere zwei DIVs. Die Eingabevariable ist aber ein SSE Type. Warum sind dann auch zwei DIVs im Assember Code zu sehen? Na weil zwei Divisionen im C++ Code stehen. Um das zu verbessern muss man davon absehen zusammengehörige Dinge als einzelne Skalare zu implementieren.
auto func(const Vector<float,3>& UBAR) { Vector<float,3> UV = UBAR/UBAR[0]; return UV;
func(Vector<float, 3> const&): movss (%rdi), %xmm1 shufps $0, %xmm1, %xmm1 movaps (%rdi), %xmm0 divps %xmm1, %xmm0
Diesmal existiert nur eine DIV Instruktion für gepackte Werte. Die Shuffle Instruktion verteil den Skalar in UBAR[0] auf alle 32Bit Einheiten im SSE Register. Allerdings ist der Code etwas verwirrend. Die Variable UV ist ein Vector mit drei Elementen. Aber es werden nur zwei Benutzt. Die Semantik ist etwas kaputt.
Der Vektor UBAR stellt soll eigentlich zwei Variablen darstellen. Geschwindigkeit und Durchfluss. Wenn das Programm entsprechend modelliert wird...
struct U_t { Vector<double,2> q; double H; }; auto func(const U_t& UBAR) { Vector<double,2> v = UBAR.q/UBAR.H; return v; }
func(U_t const&): movsd 16(%rdi), %xmm1 unpcklpd %xmm1, %xmm1 movapd (%rdi), %xmm0 divpd %xmm1, %xmm0
Der Assemblercode bleibt im Prinzip gleich, nur dass jetzt ohne Verlust double statt float genutzt werden kann. Und die Semantik ist wieder hergestellt :)