Wprowadzam interpolację dwuliniową w ciasnej pętli i próbuję ją zoptymalizować za pomocą SSE, ale otrzymuję od niej zero przyspieszenia.Interpolacja dwuliniowa SSE
Oto kod taka wersja SIMD wykorzystuje prostą konstrukcję wektora, który może być zdefiniowany jako struct Vec3f { float x, y, z; }
z realizowanych operatorów mnożenie i dodatkowo:
#ifdef USE_SIMD
const Color c11 = pixelCache[y1 * size.x + x1];
const Color c12 = pixelCache[y2 * size.x + x1];
const Color c22 = pixelCache[y2 * size.x + x2];
const Color c21 = pixelCache[y1 * size.x + x2];
__declspec(align(16)) float mc11[4] = { 1.0, c11.GetB(), c11.GetG(), c11.GetR() };
__declspec(align(16)) float mc12[4] = { 1.0, c12.GetB(), c12.GetG(), c12.GetR() };
__declspec(align(16)) float mc22[4] = { 1.0, c22.GetB(), c22.GetG(), c22.GetR() };
__declspec(align(16)) float mc21[4] = { 1.0, c21.GetB(), c21.GetG(), c21.GetR() };
// scalars in vector form for SSE
const float s11 = (x2-x)*(y2-y);
const float s12 = (x2-x)*(y-y1);
const float s22 = (x-x1)*(y-y1);
const float s21 = (x-x1)*(y2-y);
__declspec(align(16)) float ms11[4] = {1.0, s11, s11, s11};
__declspec(align(16)) float ms12[4] = {1.0, s12, s12, s12};
__declspec(align(16)) float ms22[4] = {1.0, s22, s22, s22};
__declspec(align(16)) float ms21[4] = {1.0, s21, s21, s21};
__asm {
movaps xmm0, mc11
movaps xmm1, mc12
movaps xmm2, mc22
movaps xmm3, mc21
movaps xmm4, ms11
movaps xmm5, ms12
movaps xmm6, ms22
movaps xmm7, ms21
mulps xmm0, xmm4
mulps xmm1, xmm5
mulps xmm2, xmm6
mulps xmm3, xmm7
addps xmm0, xmm1
addps xmm0, xmm2
addps xmm0, xmm3
movaps mc11, xmm0
}
#else
const Vec3f c11 = toFloat(pixelCache[y1 * size.x + x1]);
const Vec3f c12 = toFloat(pixelCache[y2 * size.x + x1]);
const Vec3f c22 = toFloat(pixelCache[y2 * size.x + x2]);
const Vec3f c21 = toFloat(pixelCache[y1 * size.x + x2]);
const Vec3f colour =
c11*(x2-x)*(y2-y) +
c21*(x-x1)*(y2-y) +
c12*(x2-x)*(y-y1) +
c22*(x-x1)*(y-y1);
#endif
Przekształcając kod asm do ponownego wykorzystania zawiera rejestry (zakończyła się z tylko trzema rejestrami xmm) nie dało żadnego efektu. Próbowałem również użyć wewnętrznego:
// perform bilinear interpolation
const Vec3f c11 = toFloat(pixelCache[y1 * size.x + x1]);
const Vec3f c12 = toFloat(pixelCache[y2 * size.x + x1]);
const Vec3f c22 = toFloat(pixelCache[y2 * size.x + x2]);
const Vec3f c21 = toFloat(pixelCache[y1 * size.x + x2]);
// scalars in vector form for SSE
const float s11 = (x2-x)*(y2-y);
const float s12 = (x2-x)*(y-y1);
const float s22 = (x-x1)*(y-y1);
const float s21 = (x-x1)*(y2-y);
__m128 mc11 = _mm_set_ps(1.f, c11.b, c11.g, c11.r);
__m128 mc12 = _mm_set_ps(1.f, c12.b, c12.g, c12.r);
__m128 mc22 = _mm_set_ps(1.f, c22.b, c22.g, c22.r);
__m128 mc21 = _mm_set_ps(1.f, c21.b, c21.g, c21.r);
__m128 ms11 = _mm_set_ps(1.f, s11, s11, s11);
__m128 ms12 = _mm_set_ps(1.f, s12, s12, s12);
__m128 ms22 = _mm_set_ps(1.f, s22, s22, s22);
__m128 ms21 = _mm_set_ps(1.f, s21, s21, s21);
mc11 = _mm_mul_ps(mc11, ms11);
mc12 = _mm_mul_ps(mc12, ms12);
mc22 = _mm_mul_ps(mc22, ms22);
mc21 = _mm_mul_ps(mc21, ms21);
mc11 = _mm_add_ps(mc11, mc12);
mc11 = _mm_add_ps(mc11, mc22);
mc11 = _mm_add_ps(mc11, mc21);
Vec3f colour;
_mm_storeu_ps(colour.array, mc11);
I na próżno. Czy czegoś brakuje, czy nie można tu uzyskać większej prędkości?
Nienawidzę tego mówić, ale to zdecydowanie nie jest właściwe podejście . Po pierwsze, poświęcasz tyle pracy na zapełnianie wektorów (co jest czystym obciążeniem). Potem masz nieprzyjemny łańcuch zależności na końcu obliczeń. Ale zasadniczo podstawową kwestią jest to, że używasz pakowania z użyciem tablicy układów. Jeśli poważnie myślisz o SIMD, powinieneś rozważyć przejście na struct-of-arrays. – Mysticial
Ok, mam to o zapełnianiu wektorów, spróbuję zmienić układ danych w celu zachowania zgodności. Ale czy mógłbyś rozwinąć "łańcuch zależności na końcu obliczeń"? – SimpleMan
Masz 3 dodatki, które zależą od siebie. Więc żadna z nich nie może być wykonana równolegle, ponieważ trzeba ukończyć przed rozpoczęciem następnego. Widzę, że robisz coś w rodzaju redukcji - która jest optymalnie wykonywana przy użyciu redukcji drzewa binarnego. W tej chwili łączysz tylko 4 wektory. Nie ma więc wiele do zyskania bez względu na to, jak je zmienić. Ale podejrzewam, że na szerszym obrazie faktycznie podsumowujecie znacznie większy zbiór liczb. – Mysticial