Parallel program에서 많이 사용하는 matrix multiplication을 예제로 들어 openMP에 대한 간단한 설명을 하고자 한다.
[시작하기 전에]
openMP 프로그램의 순서는 먼저 parallelism을 형성하고 작업을 분배하는 형식이 된다.
Parallelism 형성
(MPI에서 process 여러 개 만드는 것과 같은 작업이라고 생각하면 된다)
#pragma omp parallel [clauses](clauses에 대해서는 manual이나 spec을 참조하기 바란다.)
이 구문을 사용하면 master thread는 정해진 thread 개수만큼 thread를 생성하여 아래 그림과 같은 parallel region을 형성한다. 그리고 나서 작업이 끝나면 모두 join하고 프로그램의 수행을 끝낸다.
작업 분배
이렇게 하여 형성된 parallel region에 원하는 작업을 분배한다. 작업을 분배하는 방법은 크게 두 가지로 나눌 수 있는데 ‘for’과 ‘section’이 있다.
1) #pragma omp for [clauses]이것은 data parallelism을 사용하는 것으로 이 구문으로 시작되는 부분을 모든 thread가 골고루 나누어 수행한다. 같은 작업을 여러 thread가 data를 나누어 수행하길 원할 때 사용한다. (100개의 배열을 10개의 thread가 10개씩 수행하도록 하고자 할 때)
2) #pragma omp section [clauses]이것은 functional parallelism을 사용하는 것으로 이 구문으로 시작되는 부분을 한 thread가 수행한다. Section을 여러 개 형성하고 각각을 한 개의 thread에게 분배하고 싶을 때 사용한다.
[Matrix Multiplication]
12*12 matrix A와 B를 곱하는 프로그램을 작성하였다.
간단하게 설명하면 다음과 같다.
4개의 thread를 사용하여 A의 row를 3개씩 분배한다.
각 thread는 A의 3개의 row와 B의 12개의 col을 사용하여 연산을 수행한다.
(Thread 0 은 A의 0,1,2번 row와 B의 0,1,2,..11번 col을 가지고 연산을 수행하고,
Thread 1은 A의 3,4,5번 row와 B의 0,1,2,..11번 col으로 연산을 수행한다.)
Thread가 전부 다 작업을 수행하면 matrix multiplication이 끝나고 결과를 출력한다.
#include
#include
#define NRA 12
#define NCA 12
#define NCB 12
// 12*12 matrix를 선언하기 위한 것으로 A의 col수와 row수는 12, B의 col 수는 12이고 row수는 A의 col수와 같아진다.
void main()
{
// 변수 선언
int tid,nthreads,i,j,k,chunk; // tid는 thread id, nthreads는 전체 thread의 수이다.
double a[NRA][NCA],b[NCA][NCB],c[NRA][NCB]
chunk=3; // chunk에 대해서는 나중에 설명하도록 한다.
//이 부분이 parallelism을 형성하는 부분이다. Shared()는 () 안의 변수들을 모든 thread가 share한다는 의미이고, private()는 ()안의 변수들이 thread마다 private임을 의미한다.
#pragma omp parallel shared(a,b,c,nthreads,chunk)private(tid,i,j,k)
{// 여기서부터 각자 thread들의 수행 code가 시작되는 것이다.
tid=omp_get_thread_num();
// 이렇게 해서 자신의 tid값을 알아낸다.
//그리고 master thread는 전체 thread의 개수를 출력한다.
if(tid==0)
{
nthreads=omp_get_num_threads();
// 이렇게 해서 현재 생성된 thread의 개수를 알 수 있다.
printf("Starting Matrix Multiplication with %d threads\n",nthreads);
}
// Matrix Initialization
// matrix의 초기화도 한 thread가 다 하기보다는 나눠서 해보자. Matrix를 초기화하고 싶은데, thread가 4개 생성된다고 가정하고 12*12 matrix니까 3*12만큼 나눠서 초기화를 시키는 게 좋겠다. Matirix a,b,c를 모두 다 그런 방식으로 해보도록 하자.
// 그렇다면 12*12를 4개의 thread에 어떻게 나눠줄까? 여기서 사용하는 것이 chunk이다. Chunk=3으로 앞에서 선언했고, 그 chunk에 따라 thread는 각자 data를 3개까지만 처리한다. 그렇다면 어떤 문법을 사용해서 이게 가능할까?
// 아래 진한 글씨로 된 부분을 보자. Matrix a를 초기화시키기 위한 부분인데, 첫 번째 for문에 대해서 i가 0부터 NRA까지 수행을 하는데 이 부분을 chunk만큼 thread가 나눠서 수행하는 것이다. 지금 현재 i는 12번 수행되어야 하니까 data size가 12라면, chunk가 3이니까 thread 하나당 3개씩 맡아서 수행하는 것이다.
// 옵션으로(이건 성능상 고려해야 할 프로그래밍 문법인 것 같다) static이면 chunk=3이니까 thread0,1,2,3이 data를 3개씩 나눠서 수행하고, dynamic이면 사정이 되는 녀석이 빨리 일을 해버린다. 원래는 thread 1이 해야 되는 부분인데 thread 0 이 한가하면 미리 남의 것까지 해버린다는 것이다.
#pragma omp for schedule(static,chunk)
for(i=0;i
#pragma omp for schedule(static,chunk)
for(i=0;i
#pragma omp for schedule(static,chunk)
for(i=0;i
// 자, 이제 초기화가 끝났으니 matrix의 곱셈을 해야 할 때이다.
// 곱셈도 당연히 나누어서 해야 하겠다. 12*12 matrix니까 앞에서 이야기했던 것처럼 row만 3개씩 분담해서 하도록 해보자. Thread는 4개 생성했으니까, 3개씩 나누면 되니까. Chunk=3이 되면 간단히 해결할 수 있게 된다.
// 이건 앞에서도 나왔던 문법이다. Chunk=3이니까 data를 사이 좋게 3개씩 나눠 갖고,
행렬 곱을 수행하게 된다.
#pragma omp for schedule(static,chunk)
for(i=0;i
printf("thread %d did row %d\n",tid,i);
for(j=0;j
}
}
// 이제 출력만 남았다.
printf("------------------------------------------------\n");
printf("Result Matrix:\n");
for(i=0;i
for(j=0;j
printf("\n");
}
printf("------------------------------------------------\n");
printf("Done\n");
}
댓글 없음:
댓글 쓰기