Introduction
   MIPS Pro Compilers
   Easy Parallelizations
   APO
   OpenMP
   MPT

SGI Origin 3000 수퍼컴퓨터를 이용한 병렬처리로 (아래의 내용을 읽은 후에 이동하여 읽을 것을 권장함)

병렬처리란? 

컴퓨터가 개발된 이래 컴퓨터의 두뇌에 해당되는 프로세서는 눈부신 발전을 거듭하였지만 처리해야 되는 작업이 지속적으로 대형화됨에 따라 단일 프로세서로 처리할 수 있는 능력의 한계에 도달하게 되었다. 이러한 난관을 극복하기 위하여 대두된 방법이 병렬 처리이다. 병렬 처리란 쉽게 말해서 하나의 작업을 처리하기 위해서 동시에 여러 개의 프로세서를 사용하여 작업의 처리 속도를 높이는 것을 말한다. 이렇게 다중프로세서를 동시에 사용하는 병렬처리를 통해 얻을 수 있는 성능 향상은 다음과 같이 기술될 수 있다.

하나의 프로세서를 사용하여 작업을 수행하였을 때 소요된 시간을 t라고 할때 p개의 프로세서를 사용하였을 때 이상적인 소요시간은 t/p일 것이다. 하지만 실제 병렬 처리에서 얻을 수 있는 성능은 알고리듬의 문제, 네트워크 문제 등으로 인하여 이보다 떨어지게 되는 것이 보통이다. 이러한 성능 향상으로 인하여 얻을 수 있는 가장 큰 장점은 각 분야의 연구자들이 컴퓨터의 자원부족으로 그동안 풀 수 없었던 문제들을 병렬처리를 통해 풀 수 있게 되었다는 것에 있다.

주어진 프로그램에서 데이터를 병렬 처리하느냐 또는 작업을 분할하여 병렬 처리하느냐에 따라 data parallelism 과 task parallelism으로 구분된다.

Data Parallelism

data parallelism에서는 동일한 code의 segment를 각각의 프로세서에서 동시에 실행하는 방법으로 각 프로세서는 처리해야 하는 데이터의 영역을 분할 할당 받는다. 이 방법은 Fortran에서 사용되는 Do 루프 같은 곳에 적용하여 사용할 수 있다. Data parallelism은 계산작업을 많은 수의 작은 subtask로 분산하는 방법이기 때문에 fine grain parallelism이라고도 불리운다.

다음과 같은 Fortran 프로그램은 Data parallelism의 예로 사용될 수 있다. 아래와 같은 행렬연산 문제는 data parallelism 에 가장 적합한 예제이다. 다음의 Fortran code는 행렬간의 곱셈문제이다.

Original Code

DO K=1,N

DO J=1,N

DO I=1,N

C(I,J) = C(I,J) + A(I,K)*B(K,J)
END DO
END DO
END DO

Original code에 대한 병렬화는 프로세서간에 작업을 분할 배당하기 위해 필요한 OpenMP 디렉티브(directives)를 삽입하는 방법으로 이루어진다. 위의 code는 다음과 같이 간단한 디렉티브를 삽입하여 병렬화된 code로 변경할 수 있다.

Parallel Code

!$OMP PARALLEL DO

DO K=1,N

DO J=1,N

DO I=1,N

C(I,J) = C(I,J) + A(I,K)*B(K,J)
END DO
END DO
END DO

!$END PARALLEL DO

OpenMP는 병렬 디렉티브의 표준으로 많은 하드웨어 업체들이 채택하고 있다. OpenMP 디렉티브는data parallelism과 task parallelism에 모두 적용될 수 있다. SGI사는 OpenMP의 지원 및 개발을 주도하고 있는 회사이다. OpenMP에 대한 정보를 자세히 알고 싶으면 OpenMP website(http://www.openmp.org)를 방문하면 된다.

OpenMP에서 병렬로 처리되는 루프는 디렉티브 바로 다음에 오는 루프이다. 위의 예제에서 병렬로 실행되는 루프는 K 루프 (DO K=1,N) 이다.

각 프로세서는 서로 다른 데이터 어레이 영역을 할당 받어서 실행을 하게 된다. K 루프에서 N이 20이고 사용된 프로세서가 4라고 할 때 각 프로세서는 다음과 같이 데이터 영역을 할당 받어 처리하게 된다.

Processor

Iterations of K

Data Elements

proc0

K=1:5

A(I, 1:5) B(1:5 ,J)

proc1

K=6:10

A(I, 6:10) B(6:10 ,J)

proc2

K=11:15

A(I, 11:15) B(11:15 ,J)

proc3

K=16:20

A(I, 16:20) B(16:20 ,J)

Data parallelism에서는 각 프로세서가 동일한 code segment를 실행하게 되는데 위의 예제에서는 다음과 같은 segment가 각 프로세서에서 동일하게 실행된다.

DO J=1,N

DO I=1,N

C(I,J) = C(I,J) + A(I,K)*B(K,J)
END DO
END DO

Data parallelism은 병렬화를 점진적으로(incrementally) 할 수 있다는 편리성이 있다.

점진적 병렬화의 의미는 프로그램 전체를 한꺼번에 병렬화 시키는 것이 아니라 계산량이 많은 루프부터 병렬화 시키고 원하는 병렬효율이 커질 때까지 다른 루프를 점진적으로 병렬화하여 실행할 수 있다는 것이다.

Task Parallelism

Task parallelism은 다중의 독립된 code segments를 각 프로세서에 분산하여 동시에 실행하는 방법을 쓰는 병렬 기법이다. 이 기법은 주어진 code를 여러 개의 부분들로 분할하여 각 프로세서에서 독립적으로 동시에 실행하는 것이 가능할 때 사용하는 방법이다. Task parallelism 은 계산 작업을 작은 수의 subtasks로 분할하여 실행하기 때문에 coarse grain parallelism으로 불리운다. 이 방법은 data parallelism 보다 높은 수준에서 병렬기법이 적용되기 때문에 일반적으로 병렬효율이 더 높은 방법이다. 이 방법은 parallel overhead가 작고 data parallelism에 비해 적용이 쉽다는 장점이 있지만 code의 특성상 각 부분들이 밀접하게 연결되어 데이터를 주고 받아야 되는 경우에는 적용하기 곤란한 방법이다.

다음과 같은 abstract code를 병렬화하는 것을 예를 들면,

Original Code
program main
code segment labelled A
code segment labelled B
code segment labelled C
code segment labelled D
end

이 경우에도 다음과 같이 OpenMP 디렉티브를 삽입하여 task parallelism을 구현할 수 있다.

Parallel Code
program main

!$OMP PARALLEL

!$OMP SECTIONS

code segment labelled A

!$OMP SECTION

code segment labelled B

!$OMP SECTION

code segment labelled C

!$OMP SECTION

code segment labelled D

!$OMP SECTIONS

!$OMP END PARALLEL
end

OpenMP SECTION 디렉티브를 사용하게 되면 각 SECTION은 다른 프로세서에 할당되게 된다. 위의 code를 4개의 프로세서를 사용하여 처리한다고 할 때 각 프로세서는 다음과 같은 code segments를 할당받아 처리하게 된다.

Processor

Code Segment

proc0

code segment labelled A

proc1

code segment labelled B

proc2

code segment labelled C

proc3

code segment labelled D

Amdahl’s Law

위와 같은 병렬처리 기법을 사용하여 병렬처리를 할 경우 얻을 수 있는 성능향상은 이상적인 경우에 프로세서의 수가 늘어나는 것에 비례하여 선형적으로 늘어나야 하지만 여러 가지 성능 제한 요소들로 인하여 그렇지 못한 경우가 대부분이다. 이러한 병렬 성능향상의 한계를 기술한 법칙이 Amdahl's law이다. 이 법칙에 의하면 주어진 code에서 병렬로 실행되지 않는 부분이 성능을 제한하며 성능향상의 상한선이 정해진다고 한다. 이 법칙은 다음과 같이 수식화 될 수 있다. 주어진 code에서 F % 만큼 병렬화 된다고 할 때 프로세서의 숫자가 무한개로 늘어나도code는 100/(100-F) 보다 빠르게 실행될 수 없다. 그리고 주어진 프로세서의 수 P개에서 얻을 수 있는 최대 병렬효율은 다음과 같이 표시된다.

Speed Up Ratio (Sp) = 100/(F/P+100-F) F=(0%,………….,100%)

여기서 성능 향상비인 Speed Up Ratio는 P개의 프로세서를 사용했을 경우 얻은 계산 소요시간과 1개의 프로세서를 사용했을 경우 얻은 소요시간을 나누어서 얻을 수 있는 값이다.

Speed Up Ratio (Sp) = (Elapsed time with P proc)/(Elapsed time with 1 proc)

위의 두 식을 이용하면 주어진 code에서의 병렬화 정도를 나타내는 F를 계산할 수 있고 이를 이용하면 병렬계산을 통해 얻을 수 있는 최대의 성능 향상비를 알 수 있게 된다.

병렬 프로그래밍 지원 라이브러리

C나 Fortran같은 언어를 이용하여 병렬 프로그래밍을 구현하기 위해서는 프로세서간의 통신 등을 지원하는 병렬 프로그래밍 지원 라이브러리의 도움이 필요하다. 병렬 컴퓨터 개발 초기에서 하드웨어 업체들마다 서로 다른 병렬 라이브러리를 개발하여 지원을 하였지만 병렬 컴퓨터가 확산되면서 성능 문제나 포팅(Porting) 문제 등으로 인해 많은 병렬 지원 라이브러리가 사라지고 현재에는 공유 메모리형 병렬 지원 라이브러리인 OpenMP와 분산 메모리형 병렬 지원 라이브러리인 MPI(Message Passing Interface)만이 광범위하게 사용되고 있다. OpenMP와 MPI는 각각 http://www.openmp.org 와 http://www.mpi-forum.org 라는 비영리기관에서 표준을 정하고 있고 대부분의 하드웨어 업체들이 지원을 하고 있어 이들 라이브러리를 사용하여 개발을 할 경우 여러 하드웨어에서 수정 없이 사용할 수 있다는 장점을 가질 수 있다.

OpenMP

OpenMP의 표준은 1996년에 SGI사가 공유 메모리형 병렬 컴퓨터에서 소스 코드의 호환성을 갖는 병렬 프로그래밍 인터페이스를 만들기 위해 시작되었다. 이 프로젝트는 SGI사가 슈퍼 컴퓨터의 대명사였던 Cray사를 인수하면서 벡터 컴퓨터와 스칼라 컴퓨터에서 공히 사용할 수 있는 API(Application Programming Interface)의 필요성으로 인해 개발 속도가 가속화되어 1997년에 Fortran에 대한 OpenMP의 표준이 발표되고 1998년에 C/C++에 대한 표준이 뒤이어서 발표되었다. 2000년에는 Fortran 95에 대한 표준이 OpenMP V2.0에 포함되어 발표되었다. OpenMP는 OpenMP를 지원하는 컴파일러에서 인식할 수 있는 디렉티브(Directives)기반의 API이다. 프로그램 개발자는 기존 원시 code의 수정 없이 OpenMP에서 지정된 적절한 디렉티브를 삽입함으로써 손쉽게 병렬 프로그램화 할 수 있다. OpenMP는 아래의 예와 같이 Fortran에서의 Do 루프같은 곳에 주로 사용되어 병렬화 하기 때문에 종종 Loop Level Parallelism으로 불리기도 한다.

!$omp parallel do private(xx,yy,temp) reduction(+:error)
do j = 1,m

do i = 1,n

xx = -1.0d0 + dx * dble(i-1)

yy = -1.0d0 + dy * dble(j-1)

temp = u(i,j) - (1.0-xx*xx)*(1.0-yy*yy)

error = error + temp*temp

enddo
enddo

OpenMP는 병렬 디렉티브를 프로그램의 일부분에만 삽입하는 단계적인 병렬화(Incremental Parallelism)의 적용이 가능하기 때문에 상대적으로 접근이 용이하다. 또 기존 직렬 프로그램의 수정을 최소화할 수 있고 디버깅이 쉽다는 장점을 가지고 있다.

OpenMP는 공유 메모리를 갖는 병렬 컴퓨터에서 공히 사용할 수 있는 업계의 표준이기 때문에 이식성이 뛰어난 API이다.

MPI(Message Passing Interface)

MPI는 각 프로세스간에 데이터 교환을 지원해주는 Functions(C 언어의 경우)과 Subroutines(Fortran 언어의 경우)을 모은 라이브러리를 지칭한다. MPI의 표준이 제정되기 전까지는 초병렬 컴퓨터(Massively Parallel Processor)와 같은 분산 메모리형 컴퓨터마다 서로 다른 Message Passing Library를 가지고 있어 프로그램 개발자에게 부담이 되었다. 이러한 문제점을 해결하기 위해 40여개의 기관을 대표하는 MPI Forum(http://www.mpi-forum.org)가 결성되어 2년여의 노력 끝에 개발된 것이 MPI이다. 최초의 MPI 표준은 1994년에 제정된 MPI-1 표준이고 현재에는 일방향 통신(One-Sided Communication)이 가능해진 MPI-2 표준이 개발되어 발표되었다. 이 표준은 각 함수 또는 서브루틴의 이름과 해당되는 Output, calling sequences등을 기술하고 있어 MPI 표준을 지원하는 모든 하드웨어에 개발된 프로그램을 공히 사용할 수 있는 뛰어난 이식성을 보장한다. 각 하드웨어 제조업체는 MPI 표준을 지원하면서 제품의 특성에 맞는 최적화된 MPI 버전을 탑재하고 있다.

MPI 프로그램은 일반적으로 다음과 같은 형태를 가지고 있다.

include MPI header file

variable declarations

initialize the MPI environment

...do computation and MPI communication calls...
close MPI communications

MPI header file로 C에서는 #include <mpi.h>, Fortran에서는 INCLUDE 'mpif.h'를 사용한다.

이 파일에는 모든 MPI Function과 Subroutine에 대한 정의가 되어 있다. 이를 정의한 후에 MPI를 사용하기 위해서는 반드시 MPI 환경을 초기화 시켜줘야 하고 프로그램 마지막에는 MPI를 종료 시켜줘야 한다. MPI의 초기화에는 MPI_Init을 사용하고 MPI_Comm_rank, MPI_Comm_size를 사용하여 MPI의 실행을 위한 초기 환경을 구축한다. 이후에 MPI_RECV나 MPI_SEND같은 메시지 통신함수를 이용하여 병렬계산을 수행하고 MPI_FINALIZE를 불러 MPI 프로그램을 종료시킨다.

MPI는 단순히 디렉티브만을 삽입하는 OpenMP와는 달리 개발자가 많은 것을 지정해줘야 한다. MPI는 단계적 병렬화를 구현할 수 없고 기존 직렬 프로그램의 수정이 많이 필요해 개발시간이 오래 걸리는 단점이 있지만 일단 개발이 완료되면 여러 하드웨어에서 사용할 수 있고 병렬 성능도 뛰어나다.

병렬 프로그래밍에서의 Issues

병렬 프로그램에서 좋은 병렬 효율을 보이기 위해서는 각 프로세스에 동일한 크기의 일을 수행할 수 있도록 일을 분배해야 한다. 그렇지 못하면 가장 많은 일을 할당 받은 프로세스가 종료 될 때까지 다른 프로세스는 대기 상태에 있기 때문에 병렬 효율이 크게 떨어지게 된다. 이러한 문제를 load balancing 문제라고 한다. 많은 계산문제에서 작업량을 균등하게 분배하는 것은 쉬운 일은 아니며 각 응용분야에서 적절한 분배를 위한 여러 가지 load balancing technique이 개발되어 사용되고 있다

병렬 슈퍼 컴퓨터의 성능을 제대로 활용하기 위해서는 풀고자 하는 문제의 크기가 적절해야 한다. 단일 프로세서로도 충분한 문제를 병렬로 처리할 경우 계산시간 대비에서 프로세스간의 통신에 소요되는 시간이 더 커지게 되기 때문에 기대하는 만큼의 성능이 나오지 않을 것이다.

병렬 슈퍼 컴퓨터 사용자들에 중요한 부분 중의 하나는 좋은 software tools이라고 할 수 있다. 이러한 도구들 중에 주로 사용되는 것에는 다음과 같은 것들이 있다.

- 병렬 컴파일러(parallel compilers)
- 병렬 디버거(parallel debuggers) [ PDF ]
- 성능해석도구(performance analysis tools) [ PDF ]  
- 병렬 계산 소프트웨어 라이브러리(parallel math software library)

참고 1: For performance analysis: http://techpubs.sgi.com > search speedshop (enter) 하여 찾아서 가져온 Book :  32% SpeedShop User's Guide (즉, 위의 성능해석도구란의 PDF 파일)를 클릭하여 SpeedShop 관련 명령어들의 사용법을 읽은 후, 
예를 들어,  본 서버의 /usr/demos/SpeedShop/generic 디렉토리에 있는 generic.c 파일을 본인의 HOME directory 로 copy 해 온 후,  
% cc generic.c (enter)     하여 컴파일한 후에, 
% ssrun -pcsampx a.out (enter)       하여 ssrun(SpeedShopRun)을 실행한 후,
% ls -la a.out.* (enter)      하여 새로 생성된 파일이나 사람이 읽을 수 없는 매우 긴 이름의
                                    "a.out.?숫자" 파일을 확인한 후,
% prof a.out.?숫자 (enter)   하면, a.out 실행파일의 성능을 ASCII 파일 형태로 볼 수 있다. 
참고 2: For parallel debuggers: http://trinitas.mju.ac.kr/infosearch > search workshop (enter), and refer to % /usr/demos/WorkShop directory.
참고 3: For parallel math software library: SCSL(Scientific Computing Software library) 소개 및 http://trinitas.mju.ac.kr/infosearch > search scsl (enter).
참고 4: For parallel compilers: SGI Origin 3000 수퍼컴퓨터를 이용한 병렬처리로

 
  Mail to Professor Sun Tae Soh Copyrights trinitas.mju.ac.kr All Rights Reserved.