이정훈 (renitz@gnu.org)
OpenMP 실행 모델
<그림 3>은 OpenMP의 실행 모델을 간단히 도식화한 모습이다.

<그림 3> OpenMP 실행 모델 도식도
OpenMP는 기본적으로 fork/join 모델을 따른다. OpenMp로 작성된 프로그램은 싱글 쓰레드로 시작하여 특정 지시자를 만났을 때 비로소 여러 개의 쓰레드로 나뉘어 실행된다. 이러한 여러 개의 쓰레드는 일반적으로 팀을 이루어 동작하는 것처럼 보이기 때문에 묶어서 팀이라고 호칭한다. 하나의 팀은 여러 개의 쓰레드로 구성되어 있으며, 각각의 쓰레드들은 병렬처리가 필요한 영역을 수행하여, 최종적으로 루틴이 완료되었을 경우 그 팀의 팀장(마스터 쓰레드)은 수행된 결과를 종합하여 보고하게 된다. 예를 들어 <그림 3>에서는 #pragma omp parallel 루틴 이전에는 하나의 쓰레드만 동작하며, #pragma 루틴을 만나서야 비로소 3개의 쓰레드를 발생시키게 된다. 이때 이전의 쓰레드를 마스터 쓰레드라고 하며, 이 마스터 쓰레드는 새롭게 생성된 쓰레드들의 팀장이 된다.
이제 각각의 쓰레드들은 ‘A = 1’이라는 루틴을 실행하며, 실행한 결과를 팀장에게 보고한다. 팀장은 각 쓰레드들의 보고를 종합하여 병렬 루틴을 종료하고 다음 루틴을 실행하게 된다. 결과적으로 살펴보면 실제로 병렬적으로 처리된 부분은 #pragma의 괄호로 포함된 부분임을 알 수 있다. 즉 전체 코드가 여러 쓰레드에 의해 병렬적으로 수행된 형태가 아닌 애초에 하나의 쓰레드가 존재하여 단일 쓰레드로 프로그램을 수행하다가 필요할 때에 여러 개의 쓰레드를 발생시켜 병렬 처리를 수행하는 것이다. 이러한 병렬 영역은 비단 한 프로그램에 하나만 있어야 한다는 규칙을 가지고 있지 않다. 즉, 한 프로그램에 여러 개의 병렬 영역을 포함할 수 있으며, 따라서 필요할 때마다 쓰레드들을 생성하여 병렬 수행을 할 수 있는 것이다. 이러한 병렬 영역을 나타내는 가장 대표적인 지시자는 #pragma omp parallel이다. 그렇다면 다음의 예제를 살펴보도록 하자.
이 예제는 OpenMP로 발생시킨 각각의 쓰레드에 할당되어 있는 쓰레드 ID를 출력시키는 루틴이다. #pragma 루틴에서 여러 개의 쓰레드가 발생된 후 각각의 쓰레드는 omp_get_thread_num()이라는 OpenMP 프로시저를 통해 자신에게 할당된 쓰레드 ID를 myid라는 변수에 대입한다. myid는 private으로 선언되어 있으며, 이는 각각의 쓰레드가 간직하고 있는 고유의 변수라는 것을 의미한다. 만일 myid가 private이 아니라면, 해당 팀의 공유 변수가 되기 때문에 정확한 값을 얻어낼 수 없다.
다음으로 omp_get_num_threads() 프로시저를 통해 현재 발생시킨 쓰레드의 개수를 num_thread 변수에 집어넣고, 이를 출력한다. 쓰레드 ID 값을 출력하는 과정에서 쓰레드 ID가 0인지 아닌지를 검사하게 되는데, 만일 쓰레드 ID가 0일 경우는 해당 쓰레드가 팀장이라는 의미이며, 즉 마스터 쓰레드이다. 그렇지 않으면 팀을 이루는 쓰레드들 중의 하나이며, 즉 슬레이브 쓰레드이다. 이를 실행시킨 결과는 다음과 같다.
앞의 실행 결과에서는 총 4개의 쓰레드를 생성시켰고, 각각의 쓰레드 ID를 제대로 출력하고 있음을 살펴 볼 수 있다. 여기에서 우리가 살펴 볼 수 있는 것은 앞의 코드도 OpenMP 관련 지시자와 프로시저를 제거하면 일반적인 C 프로그램 소스 코드와 같다는 점이다. 이를 앞의 pThread 코드를 약간 변형하여 다시 작성해 보도록 하자.
해당 시스템에 가장 적합한 방식으로
지금까지 공유 메모리 방식을 사용한 병렬 프로그래밍 기법에 대해 살펴보았다. 공유 메모리 방식은 SMP와 같은 집중형 메모리 시스템에서 논리적으로 공유된 메모리 주소공간을 활용하는 병렬 프로그래밍으로서 빠른 속도의 쓰레드/프로세스 간 통신이 보장됨에 따라 병렬 처리의 효과가 크다. 하지만 하드웨어 적으로 멀티 프로세서 환경이 보장되어야 하며, 프로세서 개수에 따라 병렬처리를 수행할 수 있는 정도가 제약받을 수밖에 없다는 한계도 가지고 있다. 또한 병렬 프로그래밍은 수행되는 해당 시스템의 아키텍처의 성격에 맞추어 제작되어야 하며, 병렬 시스템의 아키텍처는 해당 시스템의 구성방법에 따라 너무나 다양하므로 어느 한 가지에 일반화할 수 없다는 특징을 갖고 있다. 따라서 해당 시스템에 가장 적합한 방식을 찾아 사용해야만 한다.
정리|강경수|egy@korea.cnet.com
OpenMP 실행 모델
<그림 3>은 OpenMP의 실행 모델을 간단히 도식화한 모습이다.

OpenMP는 기본적으로 fork/join 모델을 따른다. OpenMp로 작성된 프로그램은 싱글 쓰레드로 시작하여 특정 지시자를 만났을 때 비로소 여러 개의 쓰레드로 나뉘어 실행된다. 이러한 여러 개의 쓰레드는 일반적으로 팀을 이루어 동작하는 것처럼 보이기 때문에 묶어서 팀이라고 호칭한다. 하나의 팀은 여러 개의 쓰레드로 구성되어 있으며, 각각의 쓰레드들은 병렬처리가 필요한 영역을 수행하여, 최종적으로 루틴이 완료되었을 경우 그 팀의 팀장(마스터 쓰레드)은 수행된 결과를 종합하여 보고하게 된다. 예를 들어 <그림 3>에서는 #pragma omp parallel 루틴 이전에는 하나의 쓰레드만 동작하며, #pragma 루틴을 만나서야 비로소 3개의 쓰레드를 발생시키게 된다. 이때 이전의 쓰레드를 마스터 쓰레드라고 하며, 이 마스터 쓰레드는 새롭게 생성된 쓰레드들의 팀장이 된다.
이제 각각의 쓰레드들은 ‘A = 1’이라는 루틴을 실행하며, 실행한 결과를 팀장에게 보고한다. 팀장은 각 쓰레드들의 보고를 종합하여 병렬 루틴을 종료하고 다음 루틴을 실행하게 된다. 결과적으로 살펴보면 실제로 병렬적으로 처리된 부분은 #pragma의 괄호로 포함된 부분임을 알 수 있다. 즉 전체 코드가 여러 쓰레드에 의해 병렬적으로 수행된 형태가 아닌 애초에 하나의 쓰레드가 존재하여 단일 쓰레드로 프로그램을 수행하다가 필요할 때에 여러 개의 쓰레드를 발생시켜 병렬 처리를 수행하는 것이다. 이러한 병렬 영역은 비단 한 프로그램에 하나만 있어야 한다는 규칙을 가지고 있지 않다. 즉, 한 프로그램에 여러 개의 병렬 영역을 포함할 수 있으며, 따라서 필요할 때마다 쓰레드들을 생성하여 병렬 수행을 할 수 있는 것이다. 이러한 병렬 영역을 나타내는 가장 대표적인 지시자는 #pragma omp parallel이다. 그렇다면 다음의 예제를 살펴보도록 하자.
int main (void) {
int myid, num_threads;
#pragma omp parallel private(myid, num_threads)
{
myid = omp_get_thread_num();
num_threads = omp_get_num_threads();
printf ( ThreadID %d, out of %d threads ,
myid, num_threads);
if (myid == 0)
Printf( I am the master threads. );
else
Printf( I am the salve thread. );
}
}
int myid, num_threads;
#pragma omp parallel private(myid, num_threads)
{
myid = omp_get_thread_num();
num_threads = omp_get_num_threads();
printf ( ThreadID %d, out of %d threads ,
myid, num_threads);
if (myid == 0)
Printf( I am the master threads. );
else
Printf( I am the salve thread. );
}
}
이 예제는 OpenMP로 발생시킨 각각의 쓰레드에 할당되어 있는 쓰레드 ID를 출력시키는 루틴이다. #pragma 루틴에서 여러 개의 쓰레드가 발생된 후 각각의 쓰레드는 omp_get_thread_num()이라는 OpenMP 프로시저를 통해 자신에게 할당된 쓰레드 ID를 myid라는 변수에 대입한다. myid는 private으로 선언되어 있으며, 이는 각각의 쓰레드가 간직하고 있는 고유의 변수라는 것을 의미한다. 만일 myid가 private이 아니라면, 해당 팀의 공유 변수가 되기 때문에 정확한 값을 얻어낼 수 없다.
다음으로 omp_get_num_threads() 프로시저를 통해 현재 발생시킨 쓰레드의 개수를 num_thread 변수에 집어넣고, 이를 출력한다. 쓰레드 ID 값을 출력하는 과정에서 쓰레드 ID가 0인지 아닌지를 검사하게 되는데, 만일 쓰레드 ID가 0일 경우는 해당 쓰레드가 팀장이라는 의미이며, 즉 마스터 쓰레드이다. 그렇지 않으면 팀을 이루는 쓰레드들 중의 하나이며, 즉 슬레이브 쓰레드이다. 이를 실행시킨 결과는 다음과 같다.
[renitz@smtplus openmp] ./print_num_threads
ThreadID 3, out of 4 threads
I am the slave thread.
ThreadID 0, out of 4 threads
I am the master thread.
ThreadID 1, out of 4 threads
I am the slave thread.
ThreadID 2, out of 4 threads
I am the slave thread.
ThreadID 3, out of 4 threads
I am the slave thread.
ThreadID 0, out of 4 threads
I am the master thread.
ThreadID 1, out of 4 threads
I am the slave thread.
ThreadID 2, out of 4 threads
I am the slave thread.
앞의 실행 결과에서는 총 4개의 쓰레드를 생성시켰고, 각각의 쓰레드 ID를 제대로 출력하고 있음을 살펴 볼 수 있다. 여기에서 우리가 살펴 볼 수 있는 것은 앞의 코드도 OpenMP 관련 지시자와 프로시저를 제거하면 일반적인 C 프로그램 소스 코드와 같다는 점이다. 이를 앞의 pThread 코드를 약간 변형하여 다시 작성해 보도록 하자.
#include
#include
#define NUM_THREADS 5
void *print_hello (void *threadid)
{ printf (“Thread ID %d, out of %d threads ”,
(int) threadid, NUM_THREADS);
}
int main (void)
{
static pthread_t threads[NUM_THREADS];
int rc, t;
for (t = 0; t < NUM_THREADS; t++) {
rc = pthread_create( &threads[t], NULL, print_hello, (void *) t);
}
pthread_exit(NULL);
}
#include
#define NUM_THREADS 5
void *print_hello (void *threadid)
{ printf (“Thread ID %d, out of %d threads ”,
(int) threadid, NUM_THREADS);
}
int main (void)
{
static pthread_t threads[NUM_THREADS];
int rc, t;
for (t = 0; t < NUM_THREADS; t++) {
rc = pthread_create( &threads[t], NULL, print_hello, (void *) t);
}
pthread_exit(NULL);
}
해당 시스템에 가장 적합한 방식으로
지금까지 공유 메모리 방식을 사용한 병렬 프로그래밍 기법에 대해 살펴보았다. 공유 메모리 방식은 SMP와 같은 집중형 메모리 시스템에서 논리적으로 공유된 메모리 주소공간을 활용하는 병렬 프로그래밍으로서 빠른 속도의 쓰레드/프로세스 간 통신이 보장됨에 따라 병렬 처리의 효과가 크다. 하지만 하드웨어 적으로 멀티 프로세서 환경이 보장되어야 하며, 프로세서 개수에 따라 병렬처리를 수행할 수 있는 정도가 제약받을 수밖에 없다는 한계도 가지고 있다. 또한 병렬 프로그래밍은 수행되는 해당 시스템의 아키텍처의 성격에 맞추어 제작되어야 하며, 병렬 시스템의 아키텍처는 해당 시스템의 구성방법에 따라 너무나 다양하므로 어느 한 가지에 일반화할 수 없다는 특징을 갖고 있다. 따라서 해당 시스템에 가장 적합한 방식을 찾아 사용해야만 한다.
정리|강경수|egy@korea.cnet.com
"UNIX/Linux C" 카테고리의 다른 글
- C 소스의 전체 컴파일 과정 (0)2006/07/21
- static을 이해하자 (0)2006/07/10
- 공유 메모리 방식으로 구현하는 병렬 프로그래밍 - 2 (0)2005/10/14
- 공유 메모리 방식으로 구현하는 병렬 프로그래밍 - 1 (0)2005/10/14
- C99 새 기술을 통해 보는 C 언어의 미래 (0)2005/10/12

수안이의 컴퓨터 연구실



Leave your greetings.