Вы используете неверный алгоритм для умножения матриц в порядке ijk.
for(i=0; i<N; ++i){
for(j=0; j<N; ++j){
double sum = 0;
for(k=0; k<N; ++k){
sum += a[i][k] * b[k][j];
}
c[i][j]=sum;
}
}
Всякий раз, когда k увеличивается во внутреннем цикле, b
проходит по столбцу и генерирует промах кэша. Результатом является то, что у вас есть одна ошибка кэша на итерацию. Это в значительной степени будет влиять на время вычислений, а ваш алгоритм ограничен памятью.
Вы можете увеличить количество ядер, это не увеличит пропускную способность вашей памяти (за исключением небольшого увеличения размера кэша, которое может незначительно улучшить время вычислений).
Open-MP полезен, только если у вас есть проблемы с ядром, а не для вычислений, связанных с памятью.
Чтобы увидеть эффект от дополнительных ядер, вы должны использовать другой алгоритм. Например, изменив порядок итераций на ikj.
for(i=0; i<N; ++i){
for(k=0; k<N; ++k){
double r = a[i][k];
for(j=0; j<N; ++j){
c[i][j] += r * b[k][j];
}
}
}
Когда внутренний индекс (j) увеличивается, c [i] [j] и b [i] [j] перемещаются в ряд. Вместо одного промаха за итерацию у вас будет только два промаха через каждые восемь итераций, и пропускная способность памяти больше не будет ограничивающим фактором. Ваше время вычислений будет значительно сокращено и будет зависеть от количества используемых ядер.
Времени (N = 2000, р = 1): 4,62 с
Времени (N = 2000, р = 2): 3,03 с
Времени (N = 2000, р = 4): 2,34 с
икдж не единственный путь. Вы также можете использовать блокированное умножение матриц, где умножение выполняется с помощью ijk, но для небольших матриц, которые помещаются в кэш LI.
#define BL 40
for (int jj=0;jj<N;jj+=BL)
for (int kk=0;kk<N;kk+=BL)
for (i=0;i<N;i++)
{
for (j=jj;j<min(jj+BL-1,N);j++)
{
double sum=0.0;
for (k=kk;k<min(kk+BL-1,N);k++)
sum += a[i][k]*b[k][j];
c[i][j]=sum;
}
}
}
Алгоритм немного длиннее, но, поскольку он позволяет избежать потери кэша, он также ограничен ядром и может быть улучшен путем распараллеливания.
Время, необходимое (N = 2000, p = 1): 7,22
Требуемое время (N = 2000, р = 2): 3,78 с
Требуемое время (N = 2000, p = 4): 3,08 с
Но вы никогда ничего не получите, если будете использовать open-MP для проблемы с памятью.