Ускорить умножение матриц с помощью SSE2
Я хочу знать, как ускорить умножение матриц на SSE2
вот мой код
int mat_mult_simd(double *a, double *b, double *c, int n)
{
__m128d c1,c2,a1,a2,b1;
for(int i=0; i<n/2; i++){
for(int j=0; j<n/2; j++){
c1 = _mm_load_pd(c+(2*j*n)+(i+2));
c2 = _mm_load_pd(c+n+(2*j*n)+(i+2));
for(int k=0; k<n; k++){
a1 = _mm_load1_pd(a+k+(2*j*n));
a2 = _mm load1_pd(a+n+k+(2*j*n));
b1 = _mm_load_pd(b+(k*n)+(i*2));
c1 = _mm_add_pd(c1, _mm_mul_pd(a1,b1));
c2 = _mm_add_pd(c2, _mm_mul_pd(a2,b1));
}
__mm_store_pd(c+(2*j*n)+(i+2), c1);
__mm_store_pd(c+n+(2*j*n)+(i+2), c2);
}
}
return 0;
}
каждый параметр означает
'a' = вектор a (MAT_SIZE * MAT_SIZE)
'b' = вектор b (MAT_SIZE * MAT_SIZE)
'c' = вектор c (MAT_SIZE * MAT_SIZE)
'n' = MAT_SIZE является константой (она всегда четная и>=2)
этот код ускоряется примерно на X4. против
int mat_mult_default(double *a, double *b, double *c, int n)
{
double t;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
t=0.0;
for(int k=0; k<n; k++)
t += a[i*n+k] * b[k*n+j];
c[i*n+j] = t;
}
}
}
но я хочу еще ускорить. Я обычно экспериментирую с MAT_SIZE 1000*1000 или 2000*2000. как я могу ускорить? Есть ли другой способ индексации? Я действительно хочу знать. Благодарю.
2 ответа
Вы можете сделать несколько вещей. Очевидным является разделение работы на несколько потоков (по 1 на ядро). Вы можете использовать OpenMP (самый простой), Intel TBB или другие многопоточные библиотеки. Это обеспечит значительное улучшение многоядерной машины.
Другое дело - посмотреть на разборку (через ваш любимый отладчик) - посмотрите, как компилятор обрабатывает все умножения, которые вы используете для индексов, некоторые из них можно устранить.
Ваш код выполняет 2 вычисления в одном цикле, попробуйте сделать больше 4 или 8, чтобы иметь лучшую локальность. Например, a1 и a2 могут быть рассчитаны с их соседями, которые уже находятся в кэше L1. Вы действительно можете загрузить их с помощью одной операции загрузки.
Убедитесь, что различные массивы выровнены по SSE (16 байт) и измените код для использования выровненных операций чтения / записи.
Я бы оставил многопоточность до конца, так как поиск ошибок труднее.
Просто используйте нужную библиотеку, такую как Intel Math Kernel Library или аналогичный высоко оптимизированный пакет линейной алгебры (OpenBLAS, AMD Core Math Library, ATLAS, ...). Они считаются более быстрыми по сравнению с рукописным кодом. Иногда они имеют даже специфичные для процессора оптимизации для наборов команд и размеров кэша. И они профессионалы в своей области. Если вы не планируете опубликовать статью по собственной оптимизации, переходите к библиотеке.
В последнем номере немецкого компьютерного журнала они утверждают, что компилятор достаточно умен, чтобы использовать SSE или AVX сам по себе. Просто напишите правильные циклы, и авто-векторизатор принесет лучшие результаты. Это верно для последнего компилятора Intel. Компилятор Microsoft слишком дамп. В некоторых случаях с правильными флагами компилятора компилятор Intel даже обнаруживает, что вы запрограммировали умножение матриц, и заменяет его правильным вызовом. Или вы должны проверить документацию, это не так сложно выучить такой пакет.