3.4. Parallel Library

3.4.1. 설치하기에 앞서

병렬 라이브러리 에 사용되는 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 를 이용하여 실행할 병렬 프로그램은 논리적, 혹은 물리적으로 클러스터 각 노드간 동일한 디렉토리에 존재해야 한다.

3.4.2. LAM-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 등은 한번씩 읽어보기를 권장한다)

3.4.3. MPICH

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) 을 참고하도록 하자.