Имею простейшую программу перемножения матриц, распараллеленную средствами OpenMP:
Код:
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <malloc.h>
int main (int argc, char *argv[])
{
int i,j,k;
float *a,*b,*c;
struct timeval timev1,timev2;
float time_seconds;
int N = atoi ( argv [ 1 ] );
int P = atoi ( argv [ 2 ] );
printf ( "N=%d P=%d\n", N, P );
a = (float*) malloc(N*N*sizeof(float));
b = (float*) malloc(N*N*sizeof(float));
c = (float*) malloc(N*N*sizeof(float));
for(i=0;i<N*N;i++) {
a[i]=0.1;
b[i]=0.2;
c[i]=0.0;
}
omp_set_num_threads(P);
gettimeofday(&timev1,NULL);
#pragma omp parallel for
for (i=0; i<N; i++)
for ( j = 0; j < N; j++)
for (k=0; k<N; k++)
c[i*N+j]+=a[i*N+k]*b[k*N+j];
gettimeofday(&timev2,NULL);
time_seconds=timev2.tv_sec-timev1.tv_sec+0.000001*(timev2.tv_usec-timev1.tv_usec);
printf("Computation time = %.4f\n",time_seconds);
printf ( "c[0,0]=%f\n", c[0] );
free(a);
free(b);
free(c);
}
Траслирую с помощью gcc ( v.4.1.2) и тестирую на 1-4 процессорах:
Код:
bash-3.2$ gcc -O3 -fopenmp -o mm_openmp mm_openmp.c
bash-3.2$ ./mm_openmp 1024 1
N=1024 P=1
Computation time = 7.9787
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 2
N=1024 P=2
Computation time = 4.4790
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 3
N=1024 P=3
Computation time = 2.9087
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 4
N=1024 P=4
Computation time = 2.3406
c[0,0]=20.480278
Замечаем, что результат вычислений (в частности, значение элемента c[0,0] ) одинаков, что и ожидаемо.
Теперь меняю порядок внутренних циклов в базовой части программы, что математически корректно:
Код:
#pragma omp parallel for
for (i=0; i<N; i++)
for (k=0; k<N; k++)
for (j=0; j<N; j++)
c[i*N+j]+=a[i*N+k]*b[k*N+j];
и снова тестирую. Получаю неправильные результаты вычисления при распараллеливании:
Код:
bash-3.2$ gcc -O3 -fopenmp -o mm_openmp mm_openmp.c
bash-3.2$ ./mm_openmp 1024 1
N=1024 P=1
Computation time = 2.3715
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 2
N=1024 P=2
Computation time = 1.5620
c[0,0]=20.600281
bash-3.2$ ./mm_openmp 1024 3
N=1024 P=3
Computation time = 0.9021
c[0,0]=20.180271
bash-3.2$ ./mm_openmp 1024 4
N=1024 P=4
Computation time = 0.6133
c[0,0]=8.039994
С другой стороны, при использовании icc всё работает корректно в обоих случаях:
а) без перестановки
Код:
bash-3.2$ icc -O3 -openmp -o mm_openmp mm_openmp.c
mm_openmp.c(34): (col. 3) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
mm_openmp.c(24): (col. 3) remark: LOOP WAS VECTORIZED.
bash-3.2$ ./mm_openmp 1024 1
N=1024 P=1
Computation time = 7.5631
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 2
N=1024 P=2
Computation time = 3.8956
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 3
N=1024 P=3
Computation time = 2.5625
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 4
N=1024 P=4
Computation time = 1.9317
c[0,0]=20.480278
б) с перестановкой
Код:
bash-3.2$ icc -O3 -openmp -o mm_openmp mm_openmp.c
mm_openmp.c(34): (col. 3) remark: OpenMP DEFINED LOOP WAS PARALLELIZED.
mm_openmp.c(24): (col. 3) remark: LOOP WAS VECTORIZED.
mm_openmp.c(38): (col. 6) remark: LOOP WAS VECTORIZED.
bash-3.2$ ./mm_openmp 1024 1
N=1024 P=1
Computation time = 0.2811
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 2
N=1024 P=2
Computation time = 0.1651
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 3
N=1024 P=3
Computation time = 0.1120
c[0,0]=20.480278
bash-3.2$ ./mm_openmp 1024 4
N=1024 P=4
Computation time = 0.0877
c[0,0]=20.480278
И чтобы это значило ?