병렬 라이브러리 에 사용되는 Middle ware 는 클러스터 시스템일 경우 크게 PVM(Parallel Virtual Machine) 과 MPI (Message Passing Interface) 로 나뉠수 있다. 양쪽 모두 관련문서가 상당히 많이 있으며 한가지 차이점을 꼽으면 PVM 은 이기종간의 시스템에서 돌아간다는 점과 MPI 는 동기종간의 클러스터 에서 고려된 라이브러리 라는 점이다. PVM VS MPI 에 관련된 많은 논쟁거리가 꽤 오래전부터 진행되어 왔고 아직도 논쟁이 벌어지는 곳도 있다. 여기서는 동기종 간의 클러스터 시스템으로 꾸밀것 이므로 MPI 를 설치 할것이다. MPI 는 병렬 라이브러리의 사실상의 표준으로 자리잡았다. 대표적인 MPI 라이브러리 에는 LAM-MPI 와 MPICH 가 많이 사용된다. 기본적으로 MPI 의 통신은 rsh 을 이용하며 병렬 프로세서간 점대점 (Point-to-Point) 통신을 한다. MPI 를 이용하여 실행할 병렬 프로그램은 논리적, 혹은 물리적으로 클러스터 각 노드간 동일한 디렉토리에 존재해야 한다.
LAM-MPI 를 설치해 보자. http://www.lam-mpi.org 에서 lam 최신버전을 받아오자. 이글을 쓰는 시점에서는 6.5.9 가 최신버전 이다. 참고로 새로운 버전이 나올때 설치 방법이 변경될수 있으므로 다운받은 파일에 있는 설치문서를 참고하기 바란다.
[root@master root]# wget http://www.lam-mpi.org/download/files/lam-6.5.9.tar.gz --15:07:35-- http://www.lam-mpi.org/download/files/lam-6.5.9.tar.gz => `lam-6.5.9.tar.gz' Resolving www.lam-mpi.org... done. Connecting to www.lam-mpi.org[129.79.245.239]:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2,184,991 [application/x-gzip] 100%[==========================================>] 2,184,991 197.43K/s ETA 00:00 15:07:52 (197.43 KB/s) - `lam-6.5.9.tar.gz' saved [2184991/2184991] [root@master root]# tar xzf lam-6.5.9.tar.gz [root@master root]# cd lam-6.5.9 [root@master root]# ./configure --prefix=/usr/local/lam ……………… [root@master root]# make ……………… [root@master root]# make install ……………… |
이제 lam-mpi 설치가 끝났다. 그러나 실행을 위해서 몇가지 설정이 남아 있다. 사용자가 LamMPI 를 사용하기 위해서는 먼저 몇가지 환경변수를 설 정해 줘야 한다. 모든 사용자가 동일하게 적용 받도록 /etc/bashrc 나 /etc/profile 에 다음을 추가해 주자.
[root@master root]# vi /etc/bashrc ……………… LAMHOME=/usr/local/lam PATH=$PATH:/usr/local/lam/bin export LAMHOME PATH |
이제 모든 사용자가 로그인 할때 LAMHOME 과 PATH 가 적당하게 설정 될 것이다. 이제 Lam 을 실행해 보도록 하자. Lam team 에서 권장하는 방법은 머신네임 이 기술되어 있는 파일을 만들어서 lamd 데몬과 동시에 부팅 시키는 것이다.
[root@master root]# vi /etc/lamhosts node00 node01 ……… node06 |
위와 같이 lamhosts 파일을 만들어서 lamd 의 시동 프로그램인 lamboot 에 파라메터를 넘겨주도록 하자. Lamd 데몬의 실행방법은 다음과 같다. Root 계정이 아닌 일반 계정으로 실행해야 한다.
[micro@master micro]$ lamboot -v -b /etc/lamhosts LAM 6.5.9/MPI 2 C++/ROMIO - Indiana University Executing hboot on n0 (node00 - 1 CPU)... Executing hboot on n1 (node01 - 1 CPU)... Executing hboot on n2 (node02 - 1 CPU)... Executing hboot on n3 (node03 - 1 CPU)... Executing hboot on n4 (node04 - 1 CPU)... Executing hboot on n5 (node05 - 1 CPU)... Executing hboot on n6 (node06 - 1 CPU)... topology done |
위와 같은 메시지가 나와야 한다. 그리고 실행되어 있는 lamd 의 상태를 보려면 lamnodes 로 확인해 보면 된다.
[micro@master micro]$ lamnodes n0 node00:1 n1 node01:1 n2 node02:1 n3 node03:1 n4 node04:1 n5 node05:1 n6 node06:1 |
lamd 데몬을 실행해 봤으니 shutdown 도 해보도록 하자. Lamd 데몬의 중지는 wipe 의 실행으로 가능하다.
[micro@master micro]$ wipe -v -b /etc/lamhosts LAM 6.5.9/MPI 2 C++/ROMIO - Indiana University Executing tkill on n0 (node00)... Executing tkill on n1 (node01)... Executing tkill on n2 (node02)... Executing tkill on n3 (node03)... Executing tkill on n4 (node04)... Executing tkill on n5 (node05)... Executing tkill on n6 (node06)... |
Lam-mpi 를 이용하여 병렬 프로그램을 실행시켜 보자. Lamd 데몬을 띄운다. 필자는 간단한 shell script 를 만들어서 /etc/rc.d/init.d/ 디렉토 에 위치시키고 runlevel 이나 command 상에서 lamd 데몬을 start, stop 할수 있도록 했다. 다음은 해당 script 파일이다. 주의할 점은 여러분이 실 행하는 클러스터 환경과 필자가 구현한 환경이 서로 다를수 있으니 사용자 환경에 맞게 약간의 수정을 가해야 할것이다.
[micro@master micro]$ vi /etc/rc.d/init.d/lamd #! /bin/sh # # PATH your lam-mpi #export PATH=$PATH:/usr/local/lam/bin # Source function library. . /etc/init.d/functions # See how we were called. case "$1" in start) echo -n "Starting lamd" if [ "$UID" == "500" ];then # lam 을 실행할 일반 유저계정의 UID lamboot -v -b /etc/lamhosts else su - micro -c 'lamboot -v -b /etc/lamhosts' fi echo ;; stop) echo -n "Stopping lamd" if [ "$UID" == "500" ];then wipe -v -b /etc/lamhosts else su - micro -c 'wipe -v -b /etc/lamhosts' fi echo ;; status) echo -n "shows status nodes..." if [ "$UID" == "500" ];then lamnodes else su - micro -c 'lamnodes' fi echo ;; restart) $0 stop $0 start ;; *) echo $"Usage: $0 {start|stop|status|restart}" exit 0 esac exit $? [micro@master micro]$ /etc/rc.d/init.d/lamd start Starting lamd LAM 6.5.9/MPI 2 C++/ROMIO - Indiana University Executing hboot on n0 (node00 - 1 CPU)... Executing hboot on n1 (node01 - 1 CPU)... Executing hboot on n2 (node02 - 1 CPU)... Executing hboot on n3 (node03 - 1 CPU)... …………………… |
다음은 클러스터 프로세서 간에 간단한 메시지 전달을 주고받는 C 로 만든 MPI 프로그램 이다
/* * Laboratory for Scientific Computing * http://www.lam-mpi.org/tutorials/ * University of Notre Dame * * MPI Tutorial * The cannonical ring program * * Mail questions regarding tutorial material to mpi@lam-mpi.org */ #include <stdio.h> #include "mpi.h" /* MPI Header 를 인클루드 해야함 */ int main(int argc, char *argv[]) { MPI_Status status; int num, rank, size, tag, next, from; /* Start up MPI */ MPI_Init ( , ); MPI_Comm_rank(MPI_COMM_WORLD, ); MPI_Comm_size(MPI_COMM_WORLD, ); /* Arbitrarily choose 201 to be our tag. Calculate the */ /* rank of the next process in the ring. Use the modulus */ /* operator so that the last process "wraps around" to rank */ /* zero. */ tag = 201; next = (rank + 1) % size; from = (rank + size - 1) % size; /* If we are the "console" process, get a integer from the */ /* user to specify how many times we want to go around the */ /* ring */ if (rank == 0) { printf("Enter the number of times around the ring: "); scanf("%d", #) ; printf("Process %d sending %d to %d\n", rank, num, next); MPI_Send(#, 1, MPI_INT, next, tag, MPI_COMM_WORLD); } /* Pass the message around the ring. The exit mechanism works */ /* as follows: the message (a positive integer) is passed */ /* around the ring. Each time is passes rank 0, it is decremented. */ /* When each processes receives the 0 message, it passes it on */ /* to the next process and then quits. By passing the 0 first, */ /* every process gets the 0 message and can quit normally. */ while (1) { MPI_Recv(#, 1, MPI_INT, from, tag, MPI_COMM_WORLD, ); printf("Process %d received %d\n", rank, num); if (rank == 0) { num--; printf("Process 0 decremented num\n"); } printf("Process %d sending %d to %d\n", rank, num, next); MPI_Send(#, 1, MPI_INT, next, tag, MPI_COMM_WORLD); if (num == 0) { printf("Process %d exiting\n", rank); break; } } /* The last process does one extra send to process 0, which needs */ /* to be received before the program can exit */ if (rank == 0) MPI_Recv(#, 1, MPI_INT, from, tag, MPI_COMM_WORLD, ); /* Quit */ MPI_Finalize(); return 0; } |
앞서도 얘기했듯이 병렬로 실행할 프로그램은 논리적 이던 물리적이던 프로그램을 실행할 각각 클러스터 간에 동일한 위치(Path) 에 존재해야 한다. 클러스터간에 NFS, 혹은 다른 가상 파일시스템 으로 공유중인 위치에 프로그램을 위치시키거나 혹은 특정 디렉토리에서 실행할경우 모든 노드간에 동일한 위치에 카피를 한후 실행을 하면된다. 이제 컴파일을 해보자. gcc 나 egcs 대신 lam-mpi 의 mpicc 를 이용해야 한다.
[micro@master micro]$ mpicc -o ring MPI_C_SAMPLE.c |
이제 컴파일한 병렬프로그램을 돌려보도록 하자. 프로그램 실행은 mpirun 명령으로 실행하며 mpirun 에는 여러가지 옵션이 있다. 관련 내용은 man mpirun 이나 mpirun --help 명령으로 알수가 있다.
[micro@master micro]$ mpirun -O -np 2 ./ring Enter the number of times around the ring: 150 …………………… Process 1 received 3 Process 1 sending 3 to 0 Process 0 received 3 Process 0 decremented num Process 0 sending 2 to 1 Process 1 received 2 Process 1 sending 2 to 0 Process 0 received 2 Process 0 decremented num Process 0 sending 1 to 1 Process 1 received 1 Process 1 sending 1 to 0 Process 0 received 1 Process 0 decremented num Process 0 sending 0 to 1 Process 0 exiting Process 1 received 0 Process 1 sending 0 to 0 Process 1 exiting |
lam-mpi 의 mpirun 의 -O 옵션은 프로그램을 실행할 클러스터를 기본적으로 homogeneous system (동기종) 으로 가정한다. 따라서 메시지 패싱하는데 있어서 별도의 transaction 절차를 거치지 않는다. Low type 으로 직접 통신하는 것이다. -np 2 는 프로세서의 수를 2개로 실행한다는 것이다. 해당프로그램을 2 node 시스템(CPU 가 1개 일경우) 에서 실행한다는 것을 뜻한다. 이상없이 프로그램이 돌아갔다면 기본적인 클러스터 시스템이 구성된것이다. 같은 프로그램을 여러분의 클러스터 노드 개수에 맞게도 돌려보도록 하자.
마지막으로 lam-mpi 의 관련된 여러정보는 man 을 이용 mpirun , lamclean , lamboot 등의 필요한 정보를 얻을수 있으며 또한 http://www.lam-mpi.org 에서 무수히 많은 정보를 얻을수 있다. (FAQ 나 Tutorial 등은 한번씩 읽어보기를 권장한다)
MPICH (MPI Chameleon) 을 설치해 보자. 앞서 얘기했듯이 가장 많이 사용되고 있는 MPI 라이브러리 중 하나가 바로 MPICH 이다. 다양한 하드웨어를 지원하며, 각종 시스템 퍼포먼스 벤치마크 프로그램의 척도로 도 많이 사용되고 있다. 많은 분량의 유저 Guide 와 매뉴얼이 있으며 MPICH 를 사용하게 된다면 최소한 적어도 CH_P4MPD(http://www.mcs.anl.gov/mpi/mpich/docs/mpichman-chp4mpd/mpichman-chp4mpd.htm) Device 에 관련된 Manual (대부분의 ix86 시스템에 해당한다. 이 문서에서는 ch_p4mpd device 를 선택하여 설치 할것이다.) 과 FAQ (http://www-unix.mcs.anl.gov/mpi/mpich/docs/faq.htm) 를 읽어보기를 권장한다. 준비 되었으면 소스파일을 다운로드 하자.
[micro@master share]$ wget ftp://ftp.mcs.anl.gov/pub/mpi/mpich.tar.gz |
앞축을 풀고 설치를 한다. 여기서는 /usr/local/mpich-1.2.5 에 설치를 할것이다.
[micro@master share]$ tar xzf mpich.tar.gz [micro@master share]$ cd mpich-1.2.5 [micro@master mpich-1.2.5]$ ./configure --with-device=ch_p4mpd --prefix=/usr/local/mpich-1.2.5 [micro@master mpich-1.2.5]$ make; make install |
이상없이 설치가 되었다면 /usr/local/mpich-1.2.5/share/machines.LINUX 에 사용할 호스트네임 을 적어준다.
[micro@master mpich-1.2.5]$ vi /usr/local/mpich-1.2.5/share/machines.LINUX node00 node01 node03 ……… node06 |
나머지 클러스터 에서도 같은 옵션으로 설치를 한다. 모든 클러스터에 설치가 되었다면 간단한 sample 프로그램을 돌려보도록 하자. Mpich 를 이용하여 병렬프로그램을 실행하려면 LAM-MPI 와 마찬가지로 MPICH 전용 데몬인 MPD 가 먼저 떠있어야 한다. MPD 를 실행시키자. 일반계정으로 실행시켜도 된다.
[micro@master share]$ /usr/local/mpich-1.2.5/bin/mpd & |
마스터 서버에서 mpd 를 실행시키고 나머지 서버에서는 실행방법이 약간 다르다. 먼저 mpd 데몬의 소켓 번호를 알아야 한다. 다음의 명령으로 알 수가 있다.
[micro@master share]$ /usr/local/mpich-1.2.5/bin/mpdtrace mpdtrace: master_33253: ihs=node00_33253 rhs=node00_33253 rhs2=node00_33253 gen=2 |
위에서 보면 mpd 데몬이 33253 소켓으로 바인딩 되었다는 것을 알수있다. 나머지 클러스터 에서도 mpd 데몬을 실행시켜 준다. Mpd 데몬끼리의 소 켓 통신을 위해 마스터 서버와 연동한다.
[micro@master share]$ rsh node01 [micro@node01 share]$ /usr/local/mpich/bin/mpd -h master -p 33253 & |
-h 옵션은 클러스터 마스터 서버의 hostname 을 적어주면 되며, -p 옵션 으로 소켓번호를 적어주면 된다. Node01 뿐 아니라 나머지 클러스터에서 도 mpd 를 실행시킨후 예제로된 병렬프로그램을 실행해보도록 하자. 예제 프로그램은 mpich examples 에 있는 원주율을 구하는 간단한 프로그램 이다.
[micro@master share]$ cd mpich-1.2.5/examples/basic [micro@master basic]$ make cpi [[micro@master basic]$ /usr/local/mpich-1.2.5/bin/mpirun -np 4 ./cpi Process 0 of 4 on node00 Process 1 of 4 on node06 Process 2 of 4 on node05 Process 3 of 4 on node04 pi is approximately 3.1415926544231239, Error is 0.0000000008333307 wall clock time = 0.002665 |
위와 비슷한 결과가 나와야 한다. 만일 올바른 결과가 나오지 않거나 에러 가 발생했다면, mpich 매뉴얼을 참고를 하거나, Case of Trouble (http://www-unix.mcs.anl.gov/mpi/mpich/docs/mpichman-chp4mpd/node59.htm#Node59) 을 참고하도록 하자.