최근 포토로그


Parallel Programming, OpenMP 그리고 Win32 - 2 복잡한컴퓨터이야기

흔히들 말하는 specification을 잘 읽어보면 의구심이 나는 부분이 많이 해소되죠. 하지만 규격문서를 처음부터 끝까지 읽는 것은 정말 엄청나게 힘든 일입니다. 잘 작성된 Tutorial을 통해서 알고자 하는 내용에 친근해진 이후에, Spec. 문서를 살펴보시는 게 좋겠죠. OpenMP와 관련된 spec. 문서는 www.openmp.org에서 찾아보실 수 있고 최신의 3.0 spec. 뿐만 아니라 2.5,2.0.1.0 spec도 잘 모아져 있더군요. OpenMP는 누차 말씀 드리지만 compiler directive, library function, environment variable의 집합입니다. 끝까지 번역이 필요하시다면 컴파일러 지시자, 라이브러리 함수들, 환경 변수라고 하면 살림살이 좀 나아지시겠습니까? OpenMP의 compiler directive는 앞서 예제에서 본 바와 같이 모두 다음과 같은 구조를 가집니다.

#pragma omp directive_name [clause[[,] clause] new-line

#pragma는 compiler directive가 시작된다는 의미이죠, 아마도 #pragma once , #pragma pack() 등은 익숙하시리라 생각이 드는군요. OpenMP와 관련된 compiler directive는 모두 #pragam omp로 시작합니다.(omp는 아마도 Open Multi-Processing 을 줄인 것이겠지요.) #pragma와 같은 compiler directive는 compiler가 source code를 compile하는 시점에 compiler에게 무엇인가를 지시하기 위해서 사용하게 되는데요, OpenMP와 관련되어 있는 directive들은 광범위한 code generation을 시도하게 됩니다. 따라서 #pragma omp는 단순한 directive로 보기보다 코드를 생성하기 위한 일종의 컴파일러 명령어라고 보는 편이 이해가 빠를지도 모릅니다.(C#의 attribute 처럼 보이지 않으세요?)

지난번 예제에서 #pragma omp parallel을 사용하였었는데요, 이에 대한 의미는 정확히 살펴보지 않았었습니다. 먼저 spec. 으로부터 #pragma omp parallel에 대한 사용법을 살펴보죠

#pragma omp parallel [clause[ [, ]clause] ...] new-line
      structured-block

if(scalar-expression)
private(variable-list)
firstprivate(variable-list)
default(shared | none)
shared(variable-list)
copyin(variable-list)
reduction(operator: variable-list)
num_threads(integer-expression)

OpenMP를 사용한다고 해서 모든 코드가 항상 병렬적으로 수행될 필요는 없을 것입니다. 또한 어떤 경우에는 다수의 Thread가 협업을 진행하다가, 모든 Thread가 작업을 마칠 때까지 기다려야할 경우도 있죠. 즉 OpenMP를 사용할 때 가장 먼저 해주어야 하는 부분은 과연 어느 부분부터 어느 부분까지를 병렬적으로 수행할 것인가를 알려주는 것입니다. 이런 역할을 하는 directive_name이 바로 parallel 입니다. 이러한 내용을 그림으로 옮겨 보면 다음과 같습니다.

즉 위 그림에서 Parallel Region을 나타내기 위한 directive_name이 parallel 입니다.(1~4번 구간, 5~끝까지)

위 정의를 보면 structured-block 이라는 용어가 약간 생소할 수도 있겠는데요, 그냥 C/C++의 statement라고 보시면 되고, 한 줄의 문장이나  중괄호를 이용한 block을 사용하면 됩니다. 실제로 Fortran에서의 용례를 보면 다음과 같이 시작과 끝을 명시하지만, C/C++은 끝을 명시하기 보다는 중괄호를 이용하여 범위를 지정하잖아요.

!$omp parallel
    structured-block
!$omp end parallel

#pragma omp parallel
_tprintf(_T("Hello, OpenMP!\n"));
_tprintf(_T("Bye, OpenMP!\n"));
#pragma omp parallel
{
_tprintf(_T("Hello, OpenMP!\n"));
_tprintf(_T("Bye, OpenMP!\n"));
}

위 두 개의 차이점을 아시겠죠?  왕 친절하게도 수행 결과를 나타내어 보았습니다.


image


실제로 Test를 해보면 #pragma omp parallel을 처음 만나는 시점에 Thread들을 생성합니다.(당연하겠죠) 하지만 strctured-block을 벋어나는 순간 바로 Thread를 종료하지는 않습니다. 또다시 Parallel Region이 올 수도 있으니, 매번 Thread를 생성/삭제하는 것은 성능에 영향을 미치니까요.(당연한가요?)
한가지 더 용어를 정리하자면 #pragma omp parallel 구문을 수행한 Thread를 Master Thread라고 합니다.
각각의 clause에 대해서 모두 알아보면 좋겠지만 나중으로 미루기로 하구요, 간단히 test 해볼 수 있는 num_threads와  if 에 대해서만 알아보기로 하죠, num_threads는 Parallel Region을 수행할 Thread의 갯수를 제어하기 위한 clause인데요, 임의의 수를 지정함으로써 병렬 구간에 사용할 Thread의 갯수를 제어할 수 있습니다.

#pragma omp parallel num_threads(8)
_tprintf(_T("Hello, OpenMP!\n"));

라고 작성을 하시면 다음과 같은 결과를 얻으실 수 있습니다.


image


if 는 if 문 이하의 조건문이 만족할 경우에 한하여 Parallel Region으로 설정하겠다는 의미입니다.

int i = 10;
#pragma omp parallel num_threads(8) if (i == 10)
_tprintf(_T("Hello, OpenMP!\n"));

위와 같이 코드를 작성하면 다음과 같은 결과를 얻을 수 있습니다.


image


하지만 다음과 같이 코드를 변경하면 if 문 이하의 expression이 false로 평가되기 때문에, 병렬 구간 자체를 만들지 않게 되죠.

int i = 5;
#pragma omp parallel num_threads(8) if (i == 10)
_tprintf(_T("Hello, OpenMP!\n"));

image


별게 없죠? 이를 이용하면 CPU의 core 갯수가 몇 개 이상일 경우만 병렬 구간을 구성한다던 지, 혹은 64bits machine에서만 병렬 구간을 구성하는 등의 작업을 수행할 수 있겠지요.


써 놓고 보니 별 내용이 없군요.그래도 오늘은 이만~

Parallel Programming, OpenMP 그리고 Win32 - 1

Parallel Programming, OpenMP 그리고 Win32 - 2

Parallel Programming, OpenMP 그리고 Win32 - 3

Parallel Programming, OpenMP 그리고 Win32 - 4

Parallel Programming, OpenMP 그리고 Win32 - 5

Parallel Programming, OpenMP 그리고 Win32 - 6


덧글

댓글 입력 영역


facebook 프로필 위젯

트위터 위젯