Ten jeden cykl to jest trochę złudne.
Trudno nazwać procki x86 współczesnymi, a instrukcje SSE większością ;-)
ps: Masz może gdzieś porównanie czasu wykonywania się instrukcji dla proców AMD/Intel?
Dla intela manuale są tutaj
http://www.intel.com/products/processor/manuals/ (Optimization Manual).
Co do throughput i lattency to mają je praktycznie wszystkie procki. Nie tylko x86

również PowerPC, ARM, GPU, itd. Do jednego cyklu można uprościć najwyżej proste ALU, operacje na int'ach, add, or, and, mov, itd (w prockach Intela, ALU chodzi na 2x większym zegarze niż reszta co daje 0,5 cykla na thoughput i latency). Wszystko inne wymaga kilku cykli do wykonania.
Masz na myśli zastosowanie shufps/movlhps/movhlps?
Chodzi o inną organizację danych i ich pobieranie. Przy wierszowym Vector, obliczenie dot wygląda tak:
float x = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
jest to upierdliwa operacja bo mozna wykonac rownolegle tylko mnozenie. Jeżemy napiszemy sobie uniwersalny "dot" w klasie Vector to będzie wyglądać mniej więcej tak:
// oblicz 1 dot
r1 = _mm_mul_ps( v1, v2 );
r1 = _mm_add_ps( r1, _mm_movehl_ps(r1, r1));
r1 = _mm_add_ps( r1, _mm_shuffle_ps(r1, r1, _MM_SHUFFLE(3, 2, 1, 1)));
_mm_store_ss( r1 ); // zapisz 1 float
I taki kod będzie się wykonywać dla każdego Dot, 1 mnożenie, 2 dodawania, 1 move, 1 shuffle.
Pojedyńczego Dot nie da się bardziej zoptymalizować, ale jeżeli potrzebujemy obliczyć 4 różne doty na raz, to mozemy je policzyć kolumnowo. Gdy powielimy 4x powyższy kod, to będziemy mieć 4 mnożenia, 8 dodawań, 4 move i 4 shuffle. Zamiast tego możemy zrobić obliczenia kolumnowe:
// oblicz 4 doty jednoczesnie
r1 = _mm_mul_ps( k1, k2 );
r2 = _mm_mul_ps( k1, k3 );
r3 = _mm_mul_ps( k1, k4 );
r1 = _mm_add_ps( r1, r2 );
r1 = _mm_add_ps( r1, r3 );
_mm_store_ps( r1 ); // zapisz 4 floaty (4 wyniki dla 4 dot'ow)
Niestety wymaga to troche innej organizacji danych (SoA - structure of arrays, zamiast klasycznego AoS czyli Array of Structures). Czyli zamiast:
float doty[] = {
v1x, v1y, v1z, 0,
v2x, v2y, v2z, 0,
v3x, v3y, v3z, 0,
v4x, v4y, v4z, 0,
};
Mamy
float doty[] = {
v1x, v2x, v3x, v4x,
v1y, v2y, v3y, v4y,
v1z, v2z, v3z, v4z
};
Dlatego najwięcej dające optymalizacje muszą być zaplanowane dużo wcześniej już na poziomie silnika a nie pojedyńczej klasy Vector czy Matrix.
W SSE4 w końcu intel poszedł po rozum do głowy, że może jednak ludziom nie utrudniać życia i wprowadził Dot. Ale na starszych SSE trzeba radzić sobie tak jak powyżej.