Parallel processing 使用OpenMP,并行嵌套循环运行缓慢

Parallel processing 使用OpenMP,并行嵌套循环运行缓慢,parallel-processing,fortran,openmp,Parallel Processing,Fortran,Openmp,我有一个fortran程序的一部分,它由一些嵌套循环组成,我想用OpenMP并行化 integer :: nstates , N, i, dima, dimb, dimc, a_row, b_row, b_col, c_row, row, col double complex, dimension(4,4):: mat double complex, dimension(:), allocatable :: vecin,vecout nstates = 2 N = 24 allocate(

我有一个fortran程序的一部分,它由一些嵌套循环组成,我想用OpenMP并行化

integer :: nstates , N, i, dima, dimb, dimc, a_row, b_row, b_col, c_row, row, col
double complex, dimension(4,4):: mat
double complex, dimension(:), allocatable :: vecin,vecout 

nstates = 2
N = 24

allocate(vecin(nstates**N), vecout(nstates**N))
vecin = ...some data
vecout = 0

mat = reshape([...some data...],[4,4])

dimb=nstates**2

!$OMP PARALLEL DO PRIVATE(dima,dimc,row,col,a_row,b_row,c_row,b_col) 
do i=1,N-1
    dima=nstates**(i-1)
    dimc=nstates**(N-i-1)

    do a_row = 1, dima
        do b_row = 1,dimb
            do c_row = 1,dimc
                row = ((a_row-1)*dimb + b_row - 1)*dimc + c_row
                do b_col = 1,dimb
                    col = ((a_row-1)*dimb + b_col - 1)*dimc + c_row
                    !$OMP ATOMIC
                    vecout(row) = vecout(row) + vecin(col)*mat(b_row,b_col)
                end do
            end do
        end do
    end do
end do
!$OMP END PARALLEL DO 

程序运行了,我得到的结果也是正确的,速度简直太慢了。比没有OpenMP要慢得多。我对OpenMP了解不多。我是否在使用私有或OMP原子时犯了错误?对于如何提高代码性能的每一条建议,我将不胜感激。

如果您的数组太大,并且通过自动缩减得到堆栈溢出,您可以使用可分配的临时数组自己实现缩减

正如Francois Jacq所指出的,您还存在由
dima
dimb
引起的竞态条件,这应该是私有的

double complex, dimension(:), allocatable :: tmp

!$OMP PARALLEL PRIVATE(dima,dimb,row,col,a_row,b_row,c_row,b_col,tmp)

allocate(tmp(size(vecout)))
tmp = 0

!$OMP DO
do i=1,N-1
    dima=nstates**(i-1)
    dimc=nstates**(N-i-1)

    do a_row = 1, dima
        do b_row = 1,dimb
            do c_row = 1,dimc
                row = ((a_row-1)*dimb + b_row - 1)*dimc + c_row
                do b_col = 1,dimb
                    col = ((a_row-1)*dimb + b_col - 1)*dimc + c_row
                    tmp(row) = tmp(row) + vecin(col)*mat(b_row,b_col)
                end do
            end do
        end do
    end do
end do
!$OMP END DO

!$OMP CRITICAL
vecout = vecout + tmp
!$OMP END CRITICAL
!$OMP END PARALLEL

你能试试这样的东西吗

do b_col=1,dimb
   do i=1,N-1
      dima=nstates**(i-1)
      dimc=nstates**(N-i-1)
      !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(row,col,a_row,b_row,c_row)
      do a_row = 1, dima
         do b_row = 1,dimb
            do c_row = 1,dimc
                row = ((a_row-1)*dimb + b_row - 1)*dimc + c_row
                col = ((a_row-1)*dimb + b_col - 1)*dimc + c_row
                vecout(row) = vecout(row) + vecin(col)*mat(b_row,b_col)
            enddo
         enddo
      enddo
   enddo
enddo

优点是//循环现在不会导致冲突:所有索引行都不同。

您应该查看
vecout
reduce
子句。这应该会加快速度;-)在最内部的循环中使用原子指令不是一个好主意!谢谢你的回答。为什么原子指令是个坏主意?我删除了原子指令并使用了reduce(+:vecout),这导致了分段错误。减少和数组有什么特别的地方吗?减少只适用于小数组,没有分段错误。然后需要增加堆栈大小:
ulimit-s unlimited
谢谢,这很有效。但是为什么这个方法比使用原子更快呢?变量dima和dimc应该是私有的是的,我以前认识到这一点,现在在我的问题中改变了它。从我的观点来看,它们仍然是竞争条件,因为两个线程可以有相同的行索引。请参阅我建议的解决方案。@FrancoisJacq它们可以具有相同的行索引,但它们写入不同的数组。您的解决方案很好,但是每个
上都有很多同步$omp并行do
。最后的决定应该来自绩效评估。谢谢你的回答。我将测量你和Vladimir F的解决方案的时间来比较它们。到目前为止,我能说的是,您的解决方案对我来说运行得更快,不会崩溃。你能告诉我你为什么把b_col循环排除在其他循环之外吗?当b_col循环在内部时,每个线程的行索引也应该不同,不是吗?循环b_col在//循环2外部,避免线程在修改vecout时计算相同的行索引=>争用情况(随机错误的风险)。你为什么要删去折叠条款?它只是表示接下来的三个循环可能合并到一个//one.row=((a_row-1)*dimb+b_row-1)*dimc+c_row,所以row不依赖于b_col。对于折叠问题:首先我尝试了使用折叠,然后没有。因为行不依赖于b_col,所以相同的行索引可以多次获得b_col。关于崩溃,如果你测量了有无,那么一切都没问题:评判永远是测量!在我之前的信息中,请阅读“dimb时间”,而不是“b_col时间”