Ускорить умножение матриц с помощью 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 даже обнаруживает, что вы запрограммировали умножение матриц, и заменяет его правильным вызовом. Или вы должны проверить документацию, это не так сложно выучить такой пакет.

Другие вопросы по тегам