回 帖 发 新 帖 刷新版面

主题:强隐式迭代(SIP)openmp流水线并行,开始几次迭代串并行结果还一致,后来并行怎就慢慢地发散了?

再向大家请教一个问题,强隐式迭代(SIP)的openmp流水线并行有什么特别吗?我做的时候前面几次(L=1~3)时,还和串行结果一致,后来就不一致了,偏差越来越大,以至于发散,请问什么原因?串并行结果不一致可以理解,导致最后并行发散就难以理解了。

      DO L=1,NSW(IFI)
        RESAB=0.
C      
C.....CALCULATE RESIDUAL VECTOR AND THE SUM OF ABSOLUTE VALUES
C
        DO I=2,NIM
        DO IJ=LI(I+IST)+2,LI(I+IST)+NJM
          RES(IJ)=SU(IJ)-AP(IJ)*FI(IJ)-AN(IJ)*FI(IJ+1)-
     *            AS(IJ)*FI(IJ-1)-AE(IJ)*FI(IJ+NJ)-AW(IJ)*FI(IJ-NJ)
        END DO
        END DO
C
        DO I=IOCS(K)+1,IOCS(K)+NOC(K)
          RES(IJL(I))=RES(IJL(I))-AR(I)*FI(IJR(I))
          RES(IJR(I))=RES(IJR(I))-AL(I)*FI(IJL(I))
        END DO
C
C.....FORWARD SUBSTITUTION
C
[color=FF0000][b]        omp_sync1=0
        omp_counter1=1  !
C$omp parallel private(omp_cur_thread)
        omp_cur_thread=omp_get_thread_num()
        omp_sync1(omp_cur_thread)=0
        
        if(omp_cur_thread>0)then !此if语句用于控制thread进入的循序
          do
C$omp flush(omp_sync1)
             if(omp_sync1(omp_cur_thread-1)>=omp_counter1)exit !前一个thread执行完一次循环后,该
                                         !thread跳出本循环,执行下面的迭代。
          end do
C$omp flush(RES,omp_sync1)          
        end if     
        
        DO I=2,NIM
C$omp do private(IJ) reduction(+:RESAB)        !采用omp do会对循环任务自动进行划分,如果指定各
                                          !thread的循环起始位置,不用omp do语句,只用omp parallel
                                          !也是一样,迭代次数多了,和串行结果的差别越来越大,然后发
                                          !散
        DO IJ=LI(I+IST)+2,LI(I+IST)+NJM
          RESAB=RESAB+ABS(RES(IJ))
          RES(IJ)=(RES(IJ)-LS(IJ)*RES(IJ-1)-LW(IJ)*RES(IJ-NJ))*LPR(IJ)
        END DO
C$omp end do nowait
         omp_sync1(omp_cur_thread)=omp_sync1(omp_cur_thread)+1 !加1后,下一个thread就可以跳出锁循环
C$omp flush(RES,omp_sync1)          
        END DO 
C$omp end parallel  [/b][/color]   
C  
        IF(L.EQ.1) RESOR(IFI)=RESAB
        RSM=RESAB/(RESOR(IFI)+SMALL)
C
C.....BACKWARD SUBSTITUTION AND CORRECTION OF VARIABLE
C
        DO I=NIM,2,-1
        DO IJ=LI(I+IST)+NJM,LI(I+IST)+2,-1
          RES(IJ)=RES(IJ)-UN(IJ)*RES(IJ+1)-UE(IJ)*RES(IJ+NJ)
          FI(IJ)=FI(IJ)+RES(IJ)
        END DO
        END DO
C
C.....CHECK CONVERGENCE OF INNER ITERATIONS
C
        IF(LTEST) WRITE(2,*)L,' INNER ITER, RESAB= ',RESAB
        IF(RSM.LT.SOR(IFI)) RETURN
C
      END DO

回复列表 (共15个回复)

沙发

RES数组似乎并不独立于每次循环.
 RES(IJ)=(RES(IJ)-LS(IJ)*RES(IJ-1)-LW(IJ)*RES(IJ-NJ))*LPR(IJ)
这里是不是有问题?

板凳

就是因为不独立于每次循环,所以才要用并行流水线法,但是程序除了有内迭代还有外迭代循环,前几次外循环计算完后还能与串行保持一致,后来就差别越来越大,直到发散,不知为何?

3 楼

前面第一次循环只有一个线程进入这个并行循环, 之后陆续有其他线程进入(不知道你开了多少条线程). 那RES的数据竞争就在这个时候开始了吧...(虽然reduction保证了RESAB不会出现数据竞争)

我不是很懂你的并行流水线法. 并行差不多1年没去弄了. 如果有必要的话可以介绍一下, 大家都可以在论坛交流一下. 近来比较忙, 只能偶尔上上论坛.

4 楼

怎么没见有人回答我的问题了。我的想法是:通过if(omp_sync1(omp_cur_thread-1)>=omp_counter1)exit 这个语句后,线程会陆续进入下面的循环,本程序结果是错的,但刚开始几次迭代循环串并行的差异不明显,后来差异很大。肯定是存在数据竞争导致串并行结果不一致,但是我不知道是什么地方错了。不知道大家有没有做过SIP算法求解五对角矩阵的openmp并行。这种矩阵求解方法的特点是求解过程是前后依赖的,望高手赐教!不甚感激!

5 楼

我还是那句, 后面的那个大循环如果同时存在2个或以上的线程在跑的话, 就出现数据竞争了吗?(为什么要让部分线程选循环一下再放其他进程去计算那个循环?是否这样就能保证没有数据竞争?)
你的SIP算法本身就是并行算法还是你在串行基础上改并行?

P.S. : 其他高手在等讨论到技术细节的时候才出手,你n久不来,当然没人再跟帖了.

6 楼

因为如下原因:
C$omp do private(IJ) reduction(+:RESAB)        
        DO IJ=LI(I+IST)+2,LI(I+IST)+NJM
          RESAB=RESAB+ABS(RES(IJ))
          RES(IJ)=(RES(IJ)-LS(IJ)*RES([color=FF0000]IJ-1[/color])-LW(IJ)*RES([color=FF00FF]IJ-NJ[/color]))*LPR(IJ)
        END DO
C$omp end do nowait
此处将do循环分给2个线程(我设置的线程数为2),由于RES(IJ)=(RES(IJ)-LS(IJ)*RES([color=FF0000]IJ-1[/color])-LW(IJ)*RES([color=FF00FF]IJ-NJ[/color]))*LPR(IJ)需要用到相邻线程的计算结果,为了保证计算的正确性,所以让不同的线程先后进入。

我欣然接受你给我提的意见,以前因为发帖后很久没人回复,就没有一直盯着看,以至于没能及时回复,很是抱歉。

7 楼

已经隔了挺久了,加上pfan论坛的排版确实烂,使得代码很乱. 我讲讲我的理解,不正确请指正.
omp并行开始(简单点就当是2个线程吧), id=1的线程被卡在if和do组成的代码里面等待 omp_sync1(omp_cur_thread-1)>=omp_counter1 .同时id=0的线程进入下面的循环. 
注意到 DOIJ=LI(I+IST)+2,LI(I+IST)+NJM 这个循环外层还有一个 DOI=2,NIM 循环. 当id=0的线程运行了一次I的循环之后, id=1的线程得到释放, 并且也进入循环.

问题1. 那接下来DOI=2,NIM(NIM总不会就等于2吧?假设是一个不太小的数值)并行的循环区域DOIJ=LI(I+IST)+2,LI(I+IST)+NJM就很可能有2个线程被同时运行. 我不知道NJ等等参数是什么值,是否算法上能够巧妙地回避了冲突问题. 这里我觉得应该强调一下,最好不要假定id=0的线程总是在id=1的前面跑,这样很危险.omp标准没有保证这一点,如果你的想法是基于这样的假设的话,程序就只能碰运气了.

问题2. C$omp搀漀瀀爀椀瘀愀琀攀(IJ)爀攀搀甀挀琀椀漀渀(+:RESAB), 这里的私有变量IJ为什么不在一开始就声明呢? 我没有印象omp标准里面有这么说,我也无法在标准的内容里面导出这个使用(当然我确实不知道编译器是否支持这种做法.如果你使用的是嵌套并行, 在并行区域内再声明私有变量这倒是标准支持.)我的理解是一进入并行区域私有变量就会在各自线程产生一份, 共享变量存放在公共空间.应该把变量属性放到并行区的外面. 不对请指正.

问题3. DOI=2,NIM 我没有看到楼主你对I的属性进行声明, 也就是说一般编译器默认会是shared属性.这就产生一个问题. id=0的线程改了I=2, 然后他可能跑得快一点, 而且你设置了nowait, 他又跑去修改I=3, 这时候id=1的线程才近来, 那是不是计算结果就会出错? 这个I我理解也是私有变量.

我不懂你的算法也不知道我说得对不对,剩下的调试工作靠楼主一步步调试和其他朋友帮助了.

8 楼

仁兄确实是高手!很想交你一个朋友,我的Q:190961506,有兴趣加我吧!闲话少说:你的第一个问题确实提得很好,经过昨晚的程序修改,发现只要设法保证线程执行得先后顺序,就能正确运行。
对于第二个问题以及第三个问题:其实是不存在的,我是为了保险起见,才将I,IJ声明为私有变量,后来试了一下,发现在内层,外层声明私有变量都是可以的,不声明也是可以的,系统默认循环变量就是私有变量。
感谢仁兄的宝贵意见,若今后能见面,请你喝杯酒吧!

9 楼

我不是高手, 只是论坛的高手都跑到后台了. 有兴趣的话你可以加入雪球的群,那里的气氛还不错的(就是潜水的人太多了^_^). 而且现在我已经不怎么写代码了. 

问题2和3,主要是记得标准里面没有这样的说明,循环指标也不是在并行区域内就默认是私有变量的,只有那个在语句omp do语句下才是,显然代码中的I前面没有这个并行语句. 所以可能只是你的编译器有这样的扩展而已,如果移植可能就会面对这个问题了.

10 楼

有道理,可能是编译器的问题。求雪球的群号?搜索了,找不到啊!

我来回复

您尚未登录,请登录后再回复。点此登录或注册