【问题标题】:OPENMP reproducibility with 1 or n threads1 或 n 线程的 OPENMP 重现性
【发布时间】:2016-02-18 09:33:52
【问题描述】:

我正在努力并行化一小部分具有循环和调用子例程的代码。但结果与 1 线程或 2 线程不一致。

这种类型的程序是不是必须使用锁?

!$OMP parallel private(kk,j,i,k1,j1,i1,k2,j2,i2,ic,icm,xx,yy,ydatm),shared(undef,lt,ln,nd,xd,tgrd,ndaym)
 thread_id   = OMP_GET_THREAD_NUM()
 num_threads = OMP_GET_NUM_THREADS()
 if(thread_id.eq.0) thread_id=thread_id+1
 start_no    = (thread_id * ndaym / num_threads);
 end_no      = ((thread_id + 1) * ndaym / num_threads);
 xx(:)=undef
 yy(:,:)=undef 

!$OMP DO 
 DO kk=start_no,end_no

  do j=1,nlat
   do i=1,nlon
    ic=0
    do k1=kk-nd,kk+nd
     k2=k1
     if(k2.lt.1)k2=1
     if(k2.gt.ndaym)k2=ndaym
     do j1=j-lt,j+lt
      j2=j1
      if(j2.lt.1)j2=1
      if(j2.gt.nlat)j2=nlat
      do i1=i-ln,i+ln
       i2=i1
       if(i2.lt.1)i2=1
       if(i2.gt.nlon)i2=i2-nlon
       ic=ic+1
       yy(ic,:)=xdatm(i2,j2,k2,:)
       yya(:) = yy(ic,:)
       call funcmean(yya,xx(ic),1,nmem,nmem,undef)
       if(k1.eq.kk.and.j1.eq.j.and.i1.eq.i) then
         icm=ic
         call funcsd(yya,1,nmem,nmem,undef,xx(ic),xd)
       endif
      enddo  ! for i1       
     enddo   ! for j1
    enddo   ! for k1
   call funcens(xx,yy,tgrd,nmem,icm,undef,dist)
    ydatm(i,j,kk,:)= dist(:)
  enddo !for nlon
 enddo ! for nlat   
 ENDDO !kk

!$OMP end do
deallocate(xx,yy)
!$OMP end parallel

谁能提供一些线索?

【问题讨论】:

  • 在我看来,您好像在尝试计算每个线程在外部 do 循环迭代中所占的份额,而所有这些都在计算start_noend_no。如果是这样,您宁愿错过了有关 OpenMP 的要点之一,即编译器会为您执行此操作。网上有很多很好的例子,甚至在 SO 上也有,向您展示 Fortran+OpenMP 中并行化 do 循环的常见上层结构。

标签: multithreading openmp locks


【解决方案1】:

我不确定其余代码是否正确(实际上我什至没有尝试阅读它TBH)但开头显然是错误的:

!$OMP parallel private(kk,j,i,k1,j1,i1,k2,j2,i2,ic,icm,xx,yy,ydatm),shared(undef,lt,ln,nd,xd,tgrd,ndaym)
 thread_id   = OMP_GET_THREAD_NUM()
 num_threads = OMP_GET_NUM_THREADS()
 if(thread_id.eq.0) thread_id=thread_id+1
 start_no    = (thread_id * ndaym / num_threads);
 end_no      = ((thread_id + 1) * ndaym / num_threads);

我会试着总结一下我在这个 sn-p 中发现的错误:

  • 变量thread_idnum_threadsstart_noend_no 未声明private。因此,它们隐含地是shared,这显然是非常错误的。为了避免这样的基本陷阱,我强烈建议您在 !$omp parallel 指令中使用 default(none) 子句。这肯定会让您省去很多麻烦。
  • if(thread_id.eq.0) thread_id=thread_id+1:好的,假设thread_id 已经(应该已经)声明为private。之后,两个不同的线程(线程 #0 和 #1)都将具有相同的 thread_id 值,即 1。这有什么意义?
  • 然后使用 thread_id 值计算 start_noend_no(即使声明为 private),这对于线程 #0 来说是虚假的。

先尝试修复这些问题,看看效果如何。

再看一眼其余代码让我怀疑deallocate(xx,yy) 也可能是一个问题:除非分配是在并行区域中完成的(这里看起来不是这种情况,但仍然是可能的),释放应该移到它之外。

【讨论】:

  • @HighPerformanceMark 很公平,我没有考虑这里的自动分配。但正如你所指出的,无论如何,语法似乎都是假的。
  • @HighPerformanceMark 再想一想,即使假设正确的自动分配语法,undef 如何用于此目的,而xx 似乎是一维的,yy 是二维的?这两个任务中至少有一个是行不通的,不是吗?
猜你喜欢
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-10
  • 2012-09-04
  • 2021-03-27
  • 2012-10-07
  • 1970-01-01
相关资源
最近更新 更多