В качестве учебного упражнения я пробую свои силы в ускорении кода умножения матриц с использованием SIMD на различных архитектурах. У меня странная проблема с моим кодом умножения 3D-матриц для SSE2, где его производительность колеблется между двумя крайностями, либо ~ 5 мс (ожидается), либо ~ 100 мс для 1 миллиона операций.
Единственное, что "плохо", что делает этот код, это невыровненные сохранения/загрузки и хак в конце для сохранения вектора в памяти без 4-го элемента, вытаптывающего память. Это могло бы объяснить некоторую разницу в производительности, но тот факт, что разница в производительности настолько велика, заставляет меня подозревать, что я упускаю что-то важное.
Я пробовал пару вещей, но я попробую еще раз после сна.
См. код ниже. Переменная m_matrix выравнивается по границе 16 байт.
void Matrix3x3::MultiplySSE2(Matrix3x3 &other, Matrix3x3 &output)
{
__m128 a_row, r_row;
__m128 a1_row, r1_row;
__m128 a2_row, r2_row;
const __m128 b_row0 = _mm_load_ps(&other.m_matrix[0]);
const __m128 b_row1 = _mm_loadu_ps(&other.m_matrix[3]);
const __m128 b_row2 = _mm_loadu_ps(&other.m_matrix[6]);
// Perform dot products with first row
a_row = _mm_set1_ps(m_matrix[0]);
r_row = _mm_mul_ps(a_row, b_row0);
a_row = _mm_set1_ps(m_matrix[1]);
r_row = _mm_add_ps(_mm_mul_ps(a_row, b_row1), r_row);
a_row = _mm_set1_ps(m_matrix[2]);
r_row = _mm_add_ps(_mm_mul_ps(a_row, b_row2), r_row);
_mm_store_ps(&output.m_matrix[0], r_row);
// Perform dot products with second row
a1_row = _mm_set1_ps(m_matrix[3]);
r1_row = _mm_mul_ps(a1_row, b_row0);
a1_row = _mm_set1_ps(m_matrix[4]);
r1_row = _mm_add_ps(_mm_mul_ps(a1_row, b_row1), r1_row);
a1_row = _mm_set1_ps(m_matrix[5]);
r1_row = _mm_add_ps(_mm_mul_ps(a1_row, b_row2), r1_row);
_mm_storeu_ps(&output.m_matrix[3], r1_row);
// Perform dot products with third row
a2_row = _mm_set1_ps(m_matrix[6]);
r2_row = _mm_mul_ps(a2_row, b_row0);
a2_row = _mm_set1_ps(m_matrix[7]);
r2_row = _mm_add_ps(_mm_mul_ps(a2_row, b_row1), r2_row);
a2_row = _mm_set1_ps(m_matrix[8]);
r2_row = _mm_add_ps(_mm_mul_ps(a2_row, b_row2), r2_row);
// Store only the first 3 elements in a vector so we dont trample memory
_mm_store_ss(&output.m_matrix[6], _mm_shuffle_ps(r2_row, r2_row, _MM_SHUFFLE(0, 0, 0, 0)));
_mm_store_ss(&output.m_matrix[7], _mm_shuffle_ps(r2_row, r2_row, _MM_SHUFFLE(1, 1, 1, 1)));
_mm_store_ss(&output.m_matrix[8], _mm_shuffle_ps(r2_row, r2_row, _MM_SHUFFLE(2, 2, 2, 2)));
}
mm_set1_ps
, невыровненные загрузки и скалярные хранилища, означает, что реализации не хватает потенциальной производительности. Еслиm_matrix
всегда гарантированно будет выровнено по 16 байтам, то матрица 4x4 будет занимать столько же места, сколько и все это заполнение, и она гораздо лучше подходит для SSE/SSE2. - person Chuck Walbourn   schedule 20.07.2015XMVECTOR
/XMMATRIX
, чтобы компилятор объединил их. - person Chuck Walbourn   schedule 20.07.2015