본문 바로가기
Engineer/병렬컴퓨팅

2. 논블로킹 통신, 예제(수치적분)로 배우기

by _제이빈_ 2020. 3. 24.

이 글은 블로킹과 논블로킹에 대한 이해를 바탕으로 논블로킹 통신(Nonbloking communications)의 예제를 다뤘다.

MPI 코드로 수치적분을 구현한 원본게시글을 따라가며 이해한 내용들을 기록한 문서이다.

포트란으로 병렬컴퓨팅 배우기  ←링크

 

II. MPI 집합통신과 수치적분

- Numerical Integration wtih MPI Collective Communications.

 

    MPI 집합통신 예제로 평범하고 하찮은(원본이 이렇게 소개한다.) 그리고 간단한 알고리즘인 수치적분법을 가져왔다. 부분적분을 각 프로세서에서 진행하고, 그 합을 다시 마스터 프로세서에서 수행하는 것으로 적분이 완료되는 알고리즘이다. 코드만 보고 알고리즘 이해가 어렵다면, 앞 포스트의 수치적분 개념설명(링크)을 참고하길 바란다.

 

위 그림과 함께 예제를 간단히 소개하겠다. P0 ~ P3 은 프로세서로써 각각 노란 박스안을 적분할 것이다. 그리고 그 로컬(local)적분값을 P0(마스터프로세서)로 송신해 최종 적분 값을 도출해낸다. 여기서 사용된 기법은 중앙점(Midpoint) 적분으로, 적분과정에서 사각형의 넓이들의 덧샘만 존재한다. 따라서 병렬 프로그램으로 작성하기 수월하다.

 

 

II.2 포트란 + MPI 코드

 

C 코드도 원본 포스팅에 함께 첨부되어있으니, C 사용자라면 참고하길 바란다. 

 

      Program Example1_3
c#######################################################################
c#                                                                     #
c# This is an MPI example on parallel integration to demonstrate the   #
c# use of:                                                             #
c#                                                                     #
c# * MPI_Init, MPI_Comm_rank, MPI_Comm_size, MPI_Finalize              #
c# * MPI_Recv, MPI_Isend, MPI_Wait                                     #
C# * MPI_ANY_SOURCE, MPI_ANY_TAG                                       #
c#                                                                     #
c# Dr. Kadin Tseng                                                     #
c# Scientific Computing and Visualization                              #
c# Boston University                                                   #
c# 1998                                                                #
c#                                                                     #
c#######################################################################
      implicit none
      integer n, p, i, j, proc, ierr, master, myid, tag, comm, request
      real h, a, b, integral, pi, ai, my_int, integral_sum
      include "mpif.h"  ! brings in pre-defined MPI constants, ...
      integer status(MPI_STATUS_SIZE)     ! size defined in mpif.h
      data master/0/    ! processor 0 collects integral sums from other processors

      comm = MPI_COMM_WORLD       
      call MPI_Init(ierr)                       ! MPI 활성화
      call MPI_Comm_rank(comm, myid, ierr)      ! 현 프로세서 ID 번호
      call MPI_Comm_size(comm, p, ierr)         ! 총 프로세서 개수

      pi = acos(-1.0)   !  = 3.14159...
      a = 0.0           ! 하한
      b = pi/2.         ! 상한
      n = 500           ! 한 구간 당 간격 개수
      tag = 123         ! 로컬합계 송수신 테그
      h = (b-a)/n/p     ! 한 간격의 길이

      ai = a + myid*n*h ! 구간의 하한 (*한 프로세서안에서, 여기로부터 500개의 간격 계산)
      my_int = integral(ai, h, n)    
      write(*,*)'myid=',myid,',  my_int=',my_int

      if(myid .eq. master) then               ! the following is serial
        integral_sum = my_int
        do proc=1,p-1
          call MPI_Recv( 
     &       my_int, 1, MPI_REAL,  
     &       MPI_ANY_SOURCE,      ! message source
     &       MPI_ANY_TAG,          ! message tag
     &       comm, status, ierr)      ! status identifies source, tag
          integral_sum = integral_sum + my_int
        enddo
        write(*,*)'The Integral =', integral_sum   ! sum of my_int
      else
        call MPI_Isend(   
     &     my_int, 1, MPI_REAL,    ! buffer, size, datatype
     &     master, tag,            ! destination and tag
     &     comm, request, ierr)   ! get handle for MPI_Wait to check status
        call other_work(myid)     
        ! 블로킹커뮤니케이션이면  MPI_Recv가 완료될때까지 다음작동을 하지않는데, 논블로킹인지라 이 함수를 호출하는것.
        call MPI_Wait(request, status, ierr)   ! 블로킹처럼 만들기 위해 일단 Wait.
      endif

      call MPI_Finalize(ierr)                  

      end

      subroutine other_work(myid)
      implicit none
      integer myid
      write(*,"('more work on process ',i3)") myid
      return
      end

      real function integral(ai, h, n)
      implicit none
      integer n, j
      real h, ai, aij

      integral = 0.0                ! initialize integral
      do j=0,n-1                    ! sum integrals
        aij = ai +(j+0.5)*h         ! abscissa mid-point
        integral = integral + cos(aij)*h
      enddo

      return
      end

 

여기서 MPI_ANY_SOURCE 와 MPI_ANY_TAG는 눈여겨볼만하다. 먼저 수신이 준비된 메시지들먼저 받는 역할을 한다. 메시지 수신 시 프로세스 번호를 정해줘, 이미 수신대기 중인 메시지를 무시해버리는 경우를 방지하기 위함이다. 어떤 프로세서의 메시지를 받는지는 MPI_Recv 의 반환값에 포함되어 나오므로 크게 걱정하지 않아도 된다(ex, status(MPI_SOURCE)). 여기서 테그 값은 mpif.h에 초기지정된 wild card 값이 사용되는데, 이또한 status(MPI_TAG) 로 알아볼 수 있다.

 

어쨋든 코드를 살펴보았다면 이제 컴파일을 하고,

 

$mpif90 ex_3.for -o ex_3.exe

 

모델을 구동시켜 본다.

 

$mpiexec -np 4 ex_3.exe

 

 

II.3 구동결과

 

 myid=           3 ,  my_int=  7.61204883E-02
 myid=           0 ,  my_int=  0.38268363
 myid=           2 ,  my_int=  0.21677256
 myid=           1 ,  my_int=  0.32442346
more work on process   2
more work on process   3
more work on process   1
 The Integral =   1.0000001

 

결과를 살펴보자. 마스터 프로세서에서 전체 합이 다 구해지기 전에 more work on process # 가 있는 것을 확인할 수 있다. 물론 블로킹이 되지 않은 후속 작업이 마스터 프로세서에 보내지는 행위와 상관있다면, 오류가 뜰 가능성이 높다. 이런 점을 잘 고려하여 non-blocking을 적절히 섞어주면 효율적인 계산이 가능할 것이다.

반응형

댓글