Fortran OpenMP代码比非并行版本慢得多

时间:2018-03-24 13:35:22

标签: fortran openmp

我想解决随机游走问题,所以我写了一个fortran sequental代码,现在我需要并行这段代码。

subroutine random_walk(walkers)

implicit none
include "omp_lib.h"
integer :: i, j, col, row, walkers,m,n,iter
real, dimension(:, :), allocatable :: matrix, res
real :: point, z


col = 12
row = 12


allocate (matrix(row, col), res(row, col))

! Read from file
open(2, file='matrix.txt')
    do i = 1, row
        read(2, *)(matrix(i, j), j=1,col)
    end do

res = matrix


! Solve task

!$omp parallel private(i,j,m,n,point,iter) 

!$omp do collapse(2) 

do i= 2, 11        
    do j=2, 11  

        m = i
        n = j
        iter = 1
        point = 0

        do while (iter <= walkers)
            call random_number(z)
            if (z <= 0.25) m = m - 1
            if (z > 0.25 .and. z <= 0.5) n = n +1
            if (z > 0.5 .and. z <= 0.75) m = m +1
            if (z > 0.75) n = n - 1

            if (m == 1 .or. m == 12 .or. n == 1 .or. n == 12) then 
                point = point + matrix(m, n)
                m = i
                n = j
                iter = iter + 1
            end if

        end do
        point = point / walkers           

        res(i, j) = point    
    end do        
end do

!$omp end do
!$omp end parallel    

! Write to file
open(2, file='out_omp.txt')
    do i = 1, row
        write(2, *)(res(i, j), j=1,col)
    end do    
contains    

end

因此,问题是并行程序计算的MUCH小于其顺序版本。 错误在哪里?(除了我可怕的代码)

更新:目前代码使用!$omp do指令,但结果仍然相同:它比顺序版本小得多。

1 个答案:

答案 0 :(得分:3)

最有可能的是,这种行为与随机数提取有关。 RANDOM_NUMBER Fortran过程甚至不保证是线程安全的,但由于GNU扩展,它至少在GNU编译器中是线程安全的。但无论如何,你所注意到的表现似乎非常糟糕。

如果切换到不同的线程安全随机数生成器,代码的可伸缩性可能会很好。我使用了经典的ran2.f生成器:

http://www-star.st-and.ac.uk/~kw25/research/montecarlo/ran2.f

修改

使其成为线程安全的。如果我没错,那就这样做:

    呼叫单元中的
  • 声明并定义:

    integer :: iv(32), iy, idum2, idum

    idum2 = 123456789 ; iv(:) = 0 ; iy = 0

  • 在OpenMP指令中将idum添加为私有,将idum2iviy添加为firstprivate(按照您需要添加z的方式也像私人一样)

  • 并行部分中的
  • 添加(do之前) idum = - omp_get_thread_num() 为不同的线程提供不同的随机数

  • 来自ran2函数的
  • 删除DATA和SAVE行e作为参数传递idum2, iv, iy

    FUNCTION ran2(idum, iv, iy, idum2)

  • 调用ran2而不是random_number内在函数 z = ran2(idum, iv, iy, idum2)

对于walkers = 100000(GNU编译器),这些是我的时代:

1 thread   => 4.7s
2 threads  => 2.4s
4 threads  => 1.5s
8 threads  => 0.78s
16 threads => 0.49s

与问题没有严格关联,但我必须说为每个4&#34;比特信息提取一个实数(+1或-1),条件的使用可能会改变使用更有效的策略。