Practical MPI programming (Y. Aoyama and J. Nakano) by 바죠

MPI를 활용한 프로그래밍 레퍼런스 자료입니다. 자료는 많은 분량과 각 항목에 대한 상세한 설명을 포함하고 있습니다. 비교적  잘 정리가 되어 있는 교재입니다. 위키피디아에서도 레퍼런스로 지정해둔 아티클입니다. 내용은 다양한 병렬 프로그래밍 방법을 자세히 그림을 동원하여 설명하고 있습니다. 그림들을 활용한 설명이 예술입니다. 병렬프로그램 하시는 분들에게 꼭 필요한 아티클이라고 생각합니다.

노드 36 개를 사용해서 1개월 걸리는 일을 하루에 처리할 수 있다면 대단한 일입니다. 바로 이점 때문에 우리가 병렬 컴퓨팅을 하는 것입니다. 물론, 병렬컴퓨터가 있고, 만들어진 병렬 프로그램의 병렬효율 85 %라고 가정했습니다. 이러한 특징이 우리를 병렬 컴퓨팅 프로그램에 주목하게 만드는 것이다. 과학기술 계산의 새로운 패러다임에 틀림없다. 최근 컴퓨터 센터에서 제공하는 노드들의 수는 한마디로 굉장해졌다. 1990년대 말 2000년대 초에는 8 노드, 16 노드, 32 노드 계산이 주로 이루어졌다면, 앞으로는 64 노드, 128 노드, 256 노드로 가려고 하고 있다. 문제는 병렬 효율성인데, 이렇게 갑자기 512 노드로 갈 수 없는 이유는 병렬 효율성이 급격히 떨어지기 때문이다. 보다 좋은 병렬 효율성이 주요 키워드가 될 것이다.  512 노드에서 일년을 앞서 갈 수 있는 계산을 수행할 수 있으면 좋겠다.


기본 가정: 계산량이 방대할 경우, 포트란 또는 C언어로 만들어진 프로그램을 사용해야한다. 계산속도에서는 이 두 언어를 따라올 수 있는 언어가 없다. 포트란 또는 C로 만들어진 프로그램을 실행하는데 1주일 1달이 걸린다면, 그 때 생각해야하는 것이 MPI 병렬 프로그래밍이다. 한 달 계산시간을 하루로 만들어 줄 수 있다.   

통상 3년짜리 계산이라고 말하는 경우, 365 노드* 3 일 = 1095 노드 정도의 계산을 했다고 보면 된다.
78 노드 * 14 일 = 1092 계산 정도 된다.
52 노드 *  21 일 = 1092 계산 정도 된다.


IBM 제공 자료:
온라인 버전  http://www.redbooks.ibm.com/redbooks/SG245380.html
MPI_IBM_sg245380.pdf
(Y. Aoyama and J. Nakano)  제본해서 꼭 소장해야겠습니다.

http://www.redbooks.ibm.com/redbooks/SG245380.html
http://www-unix.mcs.anl.gov/dbpp/text/node94.html#SECTION03500000000000000000

http://en.wikipedia.org/wiki/Message_Passing_Interface

MPI tutorial: http://www-unix.mcs.anl.gov/mpi/tutorial/gropp/talk.html


----------------------------------------------------------------------------

병렬 디버거:

http://andrew.ait.iastate.edu/HPC/MPI-CHECK.htm
병렬 성능 테스터기: http://www-unix.mcs.anl.gov/mpi/mpptest/

----------------------------------------------------------------------------

병렬 컴퓨팅 프로그램의 시작은 병렬 컴퓨터 구조에 대한 이해가 되어야 한다. 그렇지 않으면 병렬 컴퓨팅의 방식을 이해하기 힘들다. 병렬 컴퓨터의 구조는 "메모리+CPU"로 구성되는 계산 노드들 통신망으로 연결된 것이다. 재미있는 것은 통신망의 통신 속도가 만족스럽지 못하다. 기가비트이더넷, 미리넷 (인피니밴드) 등 크게 두 가지로 나눌 수 있다. 전자는 값이 싸고 통신 속도가 느리다. 후자는 전자와 반대라고 보면된다. 그 가격의 차이는 대략 하나의 노드를 더 추가할 수 있을 만큼이다.  응용 프로그램에 따라서는 통신이 상대적으로 많지 않은 경우가 있다. 이러한 경우 CPU의 갯수가 많을 수록 유리하겠다.



병렬 프로그래밍의 실제:
하나의 컴퓨터 프로그램을 만드는 것이다. SPMD (하나의 프로그램, 다중 데이터). 하나의 컴퓨터 프로그램은 모든 계산 노드들에 공히 사용하는 것이다. 병렬 계산이 시작되면, 각 계산 노드들은 자신이 몇 번째 노드인지를 알고 있게 된다. 그리고 모두가 공유하는 프로그램에서 자신에게 할당된 일들을 묵묵히 수행하게 된다.  


1. 완전한 순차프로그램을 만든다.
2. 병렬화를 해야하는 부분과 하지 않는 부분으로 프로그램을 분석한다. 
    (a2ps -o abc.ps abc.f90 과 같이 ps 파일로 프로그램을 출력해서 손에 들고 본다.)
3. 병렬화 해야 하는 부분에 대한 병렬화 작업을 하나씩 수행한다. (병렬 계산 결과 = 순차 계산 결과) 인풋에 관계없이 이를 만족해야한다. 계산 결과는 동일해야한다. 하지만, 결과 파일은 조금 다를 수 있다. 병렬 프로그램 출력이 순차프로그램 출력과 가능한한 동일하게 만든다. 병렬화시키는 방법에서 새로운 아이디어를 생산해본다. 병렬화 하는 방법이 한 가지가 아니라는 사실에 주목해봐야 한다. 
4. 다양한 데이터 사이즈, 다양한 "문제의 크기" 대해서 speedup을  따로 그려본다.
5. 모든 노드들이 거의 동일하게 일하도록 만들어 줘야한다. load-balancing (병렬화의 핵심 요소), 또한, 노드별로 가능한한 동일한 메모리 사용이될 수 있는지 확인해 본다. 특별히, 메모리 크기가 문제가 되는 문제를 풀 경우, 보다 '큰 문제'를 풀기 위한 방법으로 메모리를 노드별로 나누어야 한다. 메모리 양이 충분히 사용가능한 경우, 필요없이 노드별로 메모리를 나누어 사용할 필요가 없다. 나누어진 메모리 양 만큼 통신이 필요할 것이고, 이는 결국 병렬 컴퓨팅을 느리게 만들것이다. 특히, 변수들이 각 노드에서 어떻게 사용되는가?를 주목해야 한다. 이들이 노드별로 동일한 값을 가지고 있는지 아닌지를 면밀히 체크해야한다.
포트란의 경우, implicit none을 사용하고, USE, ONLY등을 사용함으로써 손쉽게 프로그램을 만들 수 있을것이다. 병렬화해야 하는 부분도 시간을 측정한 다음 가장 효과가 있을 만한 곳을 먼저 고려한다. 이 때 데이터의 크기를 염두에 두고 진행해야 한다.
1, 4, 8, 16, 32, 64 노드 정도까지 테스트 하면 될것이다.
적당히 "큰 문제"에 대해서 64 노드 정도에서 80 %이상의 speedup이 나오면 성공이라고 볼 수 있다.

speedup계산은 wall-clock 대비로 계산한다. 즉, 하나의 노드로 계산할 때, 실질적으로 걸리는 시간 (CPU 시간이 아님)과 병렬계산을 했을 때 걸리는 실질적인 시간(wall-clock) 의 비율로 표시한다.

      real*8 start,end
      start = MPI_Wtime()

  ! code to be timed
          ................................
           ......................


       end   = MPI_Wtime()
      print*,'That took ',end-start,' seconds'



사소한 계산은 CPU를 사용해서 자체적으로 만들어 내는것이 통신을 통해서 얻는 것 보다 빠를 수 있다.

사실 아래의 6개 함수를 사용해서도 많은 병렬 프로그램을 만들 수 있다.

MPI_init  | 공부할 내용이 거의 없다.

MPI_comm_size  | 공부할 내용이 거의 없다.
MPI_comm_rank  | 공부할 내용이 거의 없다.

MPI_send             | 신경을 써야하는 루틴이다. 주의를 요구한다.
MPI_recv              | 신경을 써야하는 루틴이다.  주의를 요구한다.

MPI_finalize  | 공부할 내용이 거의 없다.

데이터를 보내는 노드와 데이터를 받는 노드들 사이에 논리적인 일치가 프로그램에서 이루어지지 않으면 병렬프로그램이 이루어지지 않는다. 예를 들어 1번이 2번에게 데이터를 보내는데, 2번이 1번으로부터 데이터를 받지 않는다. 이렇게 되면 프로그램은 진도를 낼 수 없다. 프로그램이 흘러가지 않게 된다. 이러한 상황을 피해야 한다. 순차 프로그램을 하나하나씩 고쳐 나가야 한다. 한꺼번에 많을 프로그래밍을 하게 되면 에러를 찾기가 어려워진다. 한가지 바뀌고 테스트, 또 한가지 바꾸고 테스트. 이렇게 하면 쉽게 에러를 찾아 갈 수 있다. 한꺼번에 여러 가지를 테스트하면 비효율적일 수 있다.
병렬 프로그램을 조금 더 편리하게 할 수 있도록 도와주는 루틴들 , 유용한 루틴 이름: mpi_bcast, mpi_reduce, mpi_allreduce


병렬 컴퓨팅의 실질적인 아이디어: 메모리를 노드들에 걸쳐서 설정하고 사용할 수 있어야 한다. 최소한의 노드간 통신만 할 수 있어야 한다.
communication time = latency time + (message size)/(bandwidth)

latency time : 전화로 두 명이 통신을 할 때와 비교하면 쉽게 이해할 수 있다. 한 사람이 전화를 걸었을 때, 상대방이 곧바로 받지 못한다. 조금 기다렸다가 전화를 받게 된다. 이 기다리는 시간이 바로 latency time이다. 전체 병렬 계산의 입장에서 보았을 때, 아까운 시간이 흘러가고 있다. CPU 계산 속도와 비교할 때, 매우 불만족 스러운 시간이 흘러 가고 있다. 하지만, 아예 통신없는 계산은 아마도 병렬계산이 아닐것이다.  물론 그러한 계산도 있습니다. http://incredible.egloos.com/1954003


가장 많이 사용되는 do loop 병렬화의 방식을 아래에 표시했다.
do loop 병렬화 (3가지 방식)

1. block distribution

do i=n1,n2
......
end do
-------------> 아래와 같이 serial에서 parallel로 바뀝니다.
do i=ista,iend
...........
end do
여기에서 ista, iend는 노드별(irank)로 다른값이 할당된다.

subroutine para_range(n1,n2,nprocs,irank,ista,iend)
implicit none
integer n1,n2,nprocs,irank,ista,iend
integer iwork1,iwork2
iwork1=(n2-n1+1)/nprocs
iwork2=mod(n2-n1+1,nprocs)
ista=irank*iwork1+n1+min(irank,iwork2)
iend=ista+iwork1-1
if(iwork2> irank) iend=iend+1
end



2. cyclic distribution
round-robin fashion

do i=n1,n2
......
end do
-------------> 아래와 같이 serial에서 parallel로 바뀝니다.
do i=n1+irank, n2, nprocs
...........
end do


3. block-cyclic distribution

do i=n1,n2
..........
end do
-------------> 아래와 같이 serial에서 parallel로 바뀝니다.
do ii=n1+irank*iblock, n2, nprocs*iblock
do i=ii,min(ii+iblock-1,n2)
............
end do
end do

----------------------------------------------------------------------------



!234567890
       program integration
       implicit none
       include 'mpif.h'
       integer i,n
       integer myid,nproc,ierr,kount,iroot
       real*8 ff,xx,xsum,pi,hh,xpi
       character*8 fnnd ; character*10 fnnt
       integer itemp,itemq,irate

       ff(xx)= 4.0d0/(1.0d0+xx*xx)

       call MPI_init(ierr)
       call MPI_comm_size(MPI_COMM_WORLD,nproc,ierr)
       call MPI_comm_rank(MPI_COMM_WORLD,myid,ierr)

       if(myid == 0 .and. nproc > 1) print *,  nproc," processes are alive"
       if(myid == 0 .and. nproc ==1) print *,  nproc," process is alive"
       if(myid == 0)then   ! -----[   process id = 0
       call date_and_time(date=fnnd,time=fnnt)
       write(6,'(a10,2x,a8,2x,a10)') 'date,time ', fnnd,fnnt
                    endif  ! -----]   process id = 0

       if( myid == 0) then
       write(6,*) 'number of intervals?'
       read(5,*) n
       write(6,*) 'number of intervals:',n
       end if

       iroot=0
       call MPI_bcast(n,1,MPI_INTEGER, iroot, MPI_COMM_WORLD, ierr)

       hh=1.d0/float(n)
       xsum=0.0d0
       do i=myid +1, n, nproc
           xx=(float(i)-0.5d0)*hh
           xsum=xsum+ff(xx)
       end do
       xpi=hh*xsum

       call MPI_reduce(xpi,pi, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0, MPI_COMM_WORLD,ierr)

       if( myid == 0 )then
       write(6,'(a,f18.8)')  'estimated pi value', pi
       end if

       call MPI_finalize(ierr)

       end program integration

            1  process is alive
date,time   20080525  131338.468
 number of intervals?
 number of intervals:       200000
estimated pi value        3.14159265

            4  processes are alive
date,time   20080525  173433.641
 number of intervals?
 number of intervals:       200000
estimated pi value        3.14159265

----------------------------------------------------------------------------




배열 선언도 노드별로 잡아서 메모리를 절약하고 결국에는 더 큰 문제를 풀수 있다. allocate(array1(ista:iend)) 처럼 필요한 구간만 선언하여 사용할 수도 있다.


----------------------------------------------------------------------------


----------------------------------------------------------------------------



MPI tutorial:
https://computing.llnl.gov/tutorials/mpi/

Turorial material on MPI available on the Web:
http://www-unix.mcs.anl.gov/mpi/tutorial/

http://www.lam-mpi.org/tutorials/

http://www.mhpcc.edu/training/workshop/mpi/MAIN.html

http://heather.cs.ucdavis.edu/~matloff/MPI/NotesMPICH.NM.html

http://scv.bu.edu/documentation/tutorials/MPI/

http://www.personal.leeds.ac.uk/~bgy1mm/MPITutorial/MPIHome.html

http://www-unix.mcs.anl.gov/mpi/learning.html


----------------------------------------------------------------------------


추가된 자료들:
mohr_introduction_to_parallel_computing_1.pdf
mpi_for_physicist.pdf

http://www.phyast.pitt.edu/resources/labs/beowulf/Tutorial.html
MPI_ug_in_FORTRAN.pdf


http://www.democritos.it/events/computational_physics/lecture_stefano4.pdf
http://www-unix.mcs.anl.gov/mpi/usingmpi/examples/main.htm
http://pl.postech.ac.kr/~gla/cs700-07f/ref/mpi/MPITutorial.pdf

----------------------------------------------------------------------------



계산 시간(wall clock time= CPU 시간 + IO 시간 + 통신 시간) 측정과 출력
http://en.wikipedia.org/wiki/Wall_clock_time
http://incredible.egloos.com/3047318

http://www.mcs.anl.gov/research/projects/mpi/usingmpi/examples/simplempi/main.htm

      program main
      include "mpif.h"
      double precision  PI25DT
      parameter        (PI25DT = 3.141592653589793238462643d0)
      double precision  mypi, pi, h, sum, x, f, a
      double precision starttime, endtime
      integer n, myid, numprocs, i, ierr
c                                 function to integrate
      f(a) = 4.d0 / (1.d0 + a*a)


      call MPI_INIT(ierr)
      call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr)
      call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)


10   if ( myid .eq. 0 ) then
         print *, 'Enter the number of intervals: (0 quits) '
         read(*,*) n
      endif
c                                 broadcast n
      starttime = MPI_WTIME()
      call MPI_BCAST(n,1,MPI_INTEGER,0,MPI_COMM_WORLD,ierr)
c                                 check for quit signal
      if ( n .le. 0 ) goto 30
c                                 calculate the interval size
      h = 1.0d0/n
      sum  = 0.0d0
      do 20 i = myid+1, n, numprocs
         x = h * (dble(i) - 0.5d0)
         sum = sum + f(x)
20   continue
      mypi = h * sum
c                                 collect all the partial sums
      call MPI_REDUCE(mypi,pi,1,MPI_DOUBLE_PRECISION,MPI_SUM,0, MPI_COMM_WORLD,ierr)
c                                 node 0 prints the answer.
      endtime = MPI_WTIME()
      if (myid .eq. 0) then
         print *, 'pi is ', pi, ' Error is', abs(pi - PI25DT)
         print *, 'time is ', endtime-starttime, ' seconds'
      endif
      goto 10
30   call MPI_FINALIZE(ierr)
      stop
      end


----------------------------------------------------------------------------



   Fortran program for matrix-matrix multiplication
http://www.mcs.anl.gov/research/projects/mpi/usingmpi/examples/simplempi/main.htm


----------------------------------------------------------------------------

alias qa='qstat -a ; pbsnodes -a | grep "state = free" |wc'


cf.
http://incredible.egloos.com/3880690
http://incredible.egloos.com/2950371
http://incredible.egloos.com/4086075

-----------------------------------------------------------------------

load balancing


       subroutine equal_load(n1,n2,nproc,myid,istart,ifinish)
!      Written by In-Ho Lee, KRISS, September (2006)
       implicit none
       integer nproc,myid,istart,ifinish,n1,n2
       integer iw1,iw2
       iw1=(n2-n1+1)/nproc ; iw2=mod(n2-n1+1,nproc)
       istart=myid*iw1+n1+min(myid,iw2)
       ifinish=istart+iw1-1 ; if(iw2 > myid) ifinish=ifinish+1
!      print*, n1,n2,myid,nproc,istart,ifinish
       if(n2 < istart) ifinish=istart-1
       return
       end

       subroutine multiv
!      Written by In-Ho Lee, KRISS, August 10 (2007)
       USE action, ONLY : qq,force_ac,vofqj,np_ac,natom_ac,isequence,isymbol,iattyp,ii12
       IMPLICIT NONE
       include 'mpif.h'
       integer n1,n2,j,jstart,jfinish
       real*8, allocatable :: exqq(:,:,:),exforce(:,:,:),exvofqj(:)
       integer myid,nproc,ierr,kount,iroot
!
!      Tight-Binding force/energy
!      Note that the array dimensions of exsite and exforc
!
       call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr )
       call MPI_COMM_SIZE( MPI_COMM_WORLD, nproc, ierr )

       if(nproc > np_ac+1) then
       write(6,*) 'you have too many CPUs nproc, np+1',nproc,np_ac+1
       call MPI_FINALIZE(ierr)
                           stop
                           endif
       if(myid == 0)then    ! -----{ PROCESS ID = 0
       vofqj=0.0d0 ; force_ac=0.0d0
                    endif   ! -----} PROCESS ID = 0
!
       if(nproc == 1)then
       do j=0,np_ac
       call xtinker(qq(1,1,j),force_ac(1,1,j),vofqj(j),natom_ac,isequence,isymbol,iattyp,ii12)
       enddo
                     return
                     endif
!
       allocate(exqq(natom_ac,3,0:np_ac), exforce(natom_ac,3,0:np_ac), exvofqj(0:np_ac))
       if(myid == 0)then    ! -----{ PROCESS ID = 0
       exqq=qq
                        endif   ! -----} PROCESS ID = 0
       iroot=0 ; kount=3*natom_ac*(np_ac+1)
       call MPI_BCAST(exqq, kount,MPI_REAL8, iroot,MPI_COMM_WORLD,ierr)

       n1=0 ; n2=np_ac
       call equal_load(n1,n2,nproc,myid,jstart,jfinish)
!
       exforce=0.0d0 ; exvofqj=0.0d0
       do j=jstart,jfinish
       call xtinker(exqq(1,1,j),exforce(1,1,j),exvofqj(j),natom_ac,isequence,isymbol,iattyp,ii12)
       enddo

       iroot=0 ; kount=3*natom_ac*(np_ac+1)
       call MPI_REDUCE(exforce,force_ac,kount,MPI_DOUBLE_PRECISION,MPI_SUM,iroot,MPI_COMM_WORLD,ierr)
       iroot=0 ; kount=(np_ac+1)
       call MPI_REDUCE(exvofqj,vofqj,kount,MPI_DOUBLE_PRECISION,MPI_SUM,iroot,MPI_COMM_WORLD,ierr)
!      
       deallocate(exforce, exvofqj, exqq)
       return
       end


----------------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------


master-slave mode, king-soldier mode

       if(nproc > 1)then
       if(myid == 0)then   ! -----[   process id = 0
       call feedin1(nconf,iter)
                    else   ! -----    process id /= 0
       call predict1(nconf)
                    endif  ! -----]   process id /= 0
                    else
       do ip=1,ncrystals
       call grbfnn_predict(ip,tmp)
       enddo
                    endif



!234567890
       subroutine feedin1(nconf0,iter0)
       USE crystal_set, ONLY : maxnatoms,penergy_set,pstress_set,pfxyz_set
       implicit none
       include 'mpif.h'
       integer nconf0,iter0
       integer, parameter :: maxnid=2
       integer ind(maxnid),nid,id,mm,man,kid,itag,koumpi,istatus(MPI_STATUS_SIZE)

       mm=0
       do id=1,min(nproc-1,nconf0)
       mm=mm+1
!  Generation  [
!  Generation  ]
       itag=1
       call MPI_SEND(mm,1,MPI_INTEGER,mm,itag,MPI_COMM_WORLD,ierr)
       enddo

       do id=1,nconf0
       itag=3
       nid=2
       call MPI_RECV(ind,nid,MPI_INTEGER,MPI_ANY_SOURCE,itag,MPI_COMM_WORLD,istatus,ierr)
       kid=ind(1)
       man=istatus(MPI_SOURCE)
       itag=5
       koumpi=1
       call MPI_RECV(penergy_set(kid),koumpi,MPI_DOUBLE_PRECISION,man,itag,MPI_COMM_WORLD,istatus,ierr)
       koumpi=6
       call MPI_RECV(pstress_set(1,kid),koumpi,MPI_DOUBLE_PRECISION,man,itag,MPI_COMM_WORLD,istatus,ierr)
       koumpi=3*maxnatoms
       call MPI_RECV(pfxyz_set(1,1,kid),koumpi,MPI_DOUBLE_PRECISION,man,itag,MPI_COMM_WORLD,istatus,ierr)
!  Analysis  [
!  Analysis  ]
       if (mm < nconf0)then
       mm=mm+1
!  Generation  [
!  Generation  ]
       itag=1
       call MPI_SEND(mm,1,MPI_INTEGER,man,itag,MPI_COMM_WORLD,ierr)
                      else
       itag=1
       call MPI_SEND(0,1,MPI_INTEGER,man,itag,MPI_COMM_WORLD,ierr)
                      endif
       enddo
       end subroutine feedin1
!234567890
       subroutine predict1(nconf0)
       USE crystal_set, ONLY : maxnatoms,penergy_set,pstress_set,pfxyz_set
       implicit none
       include 'mpif.h'
       integer nconf0
       integer, parameter :: maxnid=2
       integer ind(maxnid),nid,id,itag,koumpi,istatus(MPI_STATUS_SIZE)
       real*8 eslave

       if (myid > nconf0) return
       do
       itag=1
       call MPI_RECV(id,1,MPI_INTEGER,0,itag,MPI_COMM_WORLD,istatus,ierr)
       if (id == 0) return

!  local   [
       call grbfnn_predict(id,eslave)
!  local   ]
       itag=3
       nid=2
       ind(2)=id
       ind(1)=id
       call MPI_SEND(ind,nid,MPI_INTEGER,0,itag,MPI_COMM_WORLD,ierr)
       itag=5
       koumpi=1
       call MPI_SEND(penergy_set(id),koumpi,MPI_DOUBLE_PRECISION,0,itag,MPI_COMM_WORLD,ierr)
       koumpi=6
       call MPI_SEND(pstress_set(1,id),koumpi,MPI_DOUBLE_PRECISION,0,itag,MPI_COMM_WORLD,ierr)
       koumpi=3*maxnatoms
       call MPI_SEND(pfxyz_set(1,1,id),koumpi,MPI_DOUBLE_PRECISION,0,itag,MPI_COMM_WORLD,ierr)
       enddo
       end subroutine predict1

-----------------------------------------------------------------------

RECV 에서 주목해야할 항목이 status 항목이다.
이것은 많은 정보를 내포하고 있다. 간단한 경우에 status 를 직접 참조하지 않고도 send - receive 가 가능하다.
하지만, 좀 더 복잡하고 일반적인 경우에는 status 를 잘 이용해야 한다.
여러 노드에서 무작위로 계산된 데이터를 보낼 때, 어디에서 데이터가 오던지 무조건 받아야만 하는 상황이 있을 수 있다.
예를 들면, 계산하는 시간이 달라서 이러한 일들이 일어날 수 있다. 빨리 계산되는 것이 있고 늦게 계산되는 경우가 있을 수 있다. 이러한 경우, 계산이 먼저 끝나면 무조건 결과를 보낼 수 있다. send 할 수 있다. 물론 곧바로 새로운 정보를 받아서 계속해서 계산을 하고자 하는 것이다.


status(MPI_SOURCE), status(MPI_TAG) and status(MPI_ERROR) contain, respectively, the source, tag and error code of the received message.

The status parameter returns additional information for some MPI routines
Additional Error status information
Additional information with wildcard parameters
C declaration               : a predefined struct
MPI_Status status;
Fortran declaration        : an array is used instead
INTEGER STATUS(MPI_STATUS_SIZE)

status(MPI_SOURCE), status(MPI_TAG) and status(MPI_ERROR) contain, respectively, the source, tag and error code of the received message.

The tag of a received message
C         :  status.MPI_TAG
Fortran : STATUS(MPI_TAG)
The source of a received message
C         : status.MPI_SOURCE
Fortran : STATUS(MPI_SOURCE)
The error code of the MPI call
C         : status.MPI_ERROR
Fortran : STATUS(MPI_ERROR)
Other uses...
-----------------------------------------------------------------------


MPI Forums

• MPI-1  Point-to-point communication, collective communication, datatypes, virtual process topologies, error codes and classes, bindings for both C and Fortran 77 (MPICH implementation) 

• MPI-1.0 May 1994 

• MPI-1.1 (minor modification) June 1995; 

• MPI-1.2 July 1997 

• MPI-1.3 (final end of MPI-1 series) May 2008 

• MPI-2   Parallel file I/O, one-sided (put/get) communication, dynamic process management, multithreaded MPI communications, bindings for Fortran 90 and C++ 

• MPI-2.0 1997 

• MPI-2.1 September 2008 

• MPI-2.2 September 2009 

• MPI-3   Nonblocking Collective operations, and others… 

• MPI-3.0 September 2012 

• MPI-3.1 June 2015


-----------------------------------------------------------------------

-----------------------------------------------------------------------




핑백

덧글

  • drfaust 2008/05/24 23:49 # 삭제 답글

    이런 좋은 자료를 올려주시다니 감사합니다. ^^;;

    MPI프로그래밍에 도전해보고 싶었는데 적당한 reference를 못 찾았었거든요.
  • 바죠 2008/05/25 12:12 # 답글

    drfaust>> 제가, 과거, 이 자료만 보고 MPI에 도전했었어도 좀 더 시행착오를 줄일수 있었지 않았나 생각하게 만드는 아주 좋은 자료입니다.
    좋은 성과 있으시길 바랍니다.
  • 바죠 2010/03/30 20:57 # 삭제 답글

    pestat
    명령어를 활용할 경우, 노드별로 CPU 사용량을 정리해 준다.
    노드별로 8개의 CPU가 있다면, 좋은 병렬 성향을 가지고 있다면. 모든 노드들에 대해서 8.0에 가까운 시간 평균 CPU사용량을 보여줄것이다.

    qstat -an
    을 활용하여 사용하고 있는 노드 번호들을 확인할 수 있다.
    ssh를 활용하여 노드에 접속한다음, top명령어를 활용하여 각 CPU의 사용 페센티지를 직접확인 한다.
  • 바죠 2011/03/29 08:36 #

    alias qa='qstat -a ; pbsnodes -a | grep "state = free" |wc'

  • 바죠 2011/02/22 14:35 # 삭제 답글

    program main
    c
    include 'mpif.h'
    c
    double precision f, a, dx, x, sum, pip, pi
    integer nx, nxp, ix
    c
    integer ierr, np, irank
    double precision starttime,endtime
    c-----------------------------------------------------------------------
    c function to be integrated
    c-----------------------------------------------------------------------
    f(a) = 4.d0 / (1.d0 + a*a)
    c-----------------------------------------------------------------------
    c MPI initialization
    c-----------------------------------------------------------------------
    call MPI_INIT(ierr)
    call MPI_COMM_SIZE(MPI_COMM_WORLD,np,ierr)
    call MPI_COMM_RANK(MPI_COMM_WORLD,irank,ierr)
    c-----------------------------------------------------------------------
    c Read number of intervals
    c-----------------------------------------------------------------------
    if (irank .eq. 0) then
    print*, 'number of intervals:'
    read(*,*) nx
    endif
    c
    starttime = MPI_WTIME()
    call MPI_BCAST(nx,1,MPI_INTEGER,0,MPI_COMM_WORLD,ierr)
    c-----------------------------------------------------------------------
    c calculate the interval size
    c-----------------------------------------------------------------------
    dx = 1.0d0 / dfloat(nx)
    nxp = nx / np
    c-----------------------------------------------------------------------
    c integration
    c-----------------------------------------------------------------------
    sum = 0.0d0
    do 10 ix = 1, nxp
    x = dx*(dfloat(irank*nxp+ix)-0.5d0)
    sum = sum + f(x)
    10 continue
    pip = dx*sum
    c collect all the partial sums
    call MPI_REDUCE(pip,pi,1,MPI_DOUBLE_PRECISION,MPI_SUM,0,
    $ MPI_COMM_WORLD,ierr)
    endtime = MPI_WTIME()
    c-----------------------------------------------------------------------
    c print pi
    c-----------------------------------------------------------------------
    if (irank .eq. 0) then
    print*, pi
    print*, endtime-starttime, ' seconds'
    endif
    c-----------------------------------------------------------------------
    c MPI finalization
    c-----------------------------------------------------------------------
    call MPI_FINALIZE(ierr)
    c-----------------------------------------------------------------------
    c end
    c-----------------------------------------------------------------------
  • 바죠 2011/02/22 14:45 # 삭제

    노드 수에 의존하는 결과
  • 바죠 2011/02/22 14:39 # 삭제 답글

    number of intervals:
    100
    3.141600986923126
    5.7935714721679688E-005 seconds
    FORTRAN STOP
    FORTRAN STOP
    [jskim@gpu15 MPI_examples]$ mpiexec -n 3 ./a.out
    number of intervals:
    100
    3.121500736926266
    8.0108642578125000E-005 seconds
    FORTRAN STOP
    FORTRAN STOP
    FORTRAN STOP
  • 바죠 2011/02/23 09:01 #

    subroutine equal_load(n1,n2,nproc,myid,istart,ifinish)
    implicit none
    integer nproc,myid,istart,ifinish,n1,n2
    integer iw1,iw2
    iw1=(n2-n1+1)/nproc ; iw2=mod(n2-n1+1,nproc)
    istart=myid*iw1+n1+min(myid,iw2)
    ifinish=istart+iw1-1 ; if(iw2 > myid) ifinish=ifinish+1
    ! print*, n1,n2,myid,nproc,istart,ifinish
    if(n2 < istart) ifinish=istart-1
    return
    end
  • 2011/02/22 15:09 # 삭제 답글 비공개

    비공개 덧글입니다.
  • 2011/02/22 16:28 # 삭제 답글 비공개

    비공개 덧글입니다.
  • 2011/03/29 17:43 # 답글 비공개

    비공개 덧글입니다.
  • 바죠 2018/06/28 11:53 # 삭제 답글

    master-slave == manager-worker (self-scheduling)

댓글 입력 영역

최근 포토로그



MathJax