MRI.COM

OpenMP スレッド並列の失敗例

omp parallel でスレッド並列化を行う場合には

が重要である。

例えば以下のようなコードは OpenMP 有効時に結果の再現性を損う可能性がある。

!$omp parallel
   do ntrc = ntrc_strt, numtrc
     if ( trim(adv_scheme(ntrc)%name)/="som" .and. trim(adv_scheme(ntrc)%name)/="ppm" ) then
       trczadvtmp(1:imu, 1:jmu, ntrc) = 0.d0
!$omp do private(i,k,kd,wt)
       do j = 2, jmu
         do i = 2, imu
          k  = ktbtm(i, j) - texnbbl(i,j)
          kd = km+1-kbbl
          wt = sign(vupp-.5D0, wlwl_total(i,j,k))
          trczadvtmp(i,j,ntrc) = atexl(i,j,kd)&
               & * (wlwl_total(i,j,k)&
               &   *((.5D0-wt)*trcbl(i,j,k,ntrc)&
               &    +(.5D0+wt)*trcbl(i,j,kd,ntrc)))
        end do
      end do
    end if
  end do
!$omp end parallel

上記コードでは、

ことが問題である。 つまり、trczadvtmp の0埋め作業が全てのスレッドで終了する前に、doループでのtrczadvtmp計算が開始される場合がある。 正しく計算された trczadvtmp が、再度0埋めされる場合があり、これによって結果の再現性が損われてしまう。

4行目と5行目の間に !omp barrier を挿入してスレッド間同期をとれば、結果の再現性は確保できる。 しかし、今回のケースでは、無用なトラブルを避けるためにも、single 指示文を用いてスレッド競合を排した以下のような実装が好ましい。

!$omp parallel
  do ntrc = ntrc_strt, numtrc
    if ( trim(adv_scheme(ntrc)%name)/="som" .and. trim(adv_scheme(ntrc)%name)/="ppm" ) then
!$omp single
      trczadvtmp(1:imu, 1, ntrc) = 0.d0
      trczadvtmp(1, 2:jmu, ntrc) = 0.d0
!$omp end single
!$omp do private(i,k,kd,wt)
      do j = 2, jmu
        do i = 2, imu
          k  = ktbtm(i, j) - texnbbl(i,j)
          kd = km+1-kbbl
          wt = sign(vupp-.5D0, wlwl_total(i,j,k))
          trczadvtmp(i,j,ntrc) = atexl(i,j,kd)&
               & * (wlwl_total(i,j,k)&
               &   *((.5D0-wt)*trcbl(i,j,k,ntrc)&
               &    +(.5D0+wt)*trcbl(i,j,kd,ntrc)))
        end do
      end do
    end if
  end do
!$omp end parallel