我想解决随机游走问题,所以我写了一个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
指令,但结果仍然相同:它比顺序版本小得多。
答案 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
添加为私有,将idum2
,iv
,iy
添加为firstprivate(按照您需要添加z
的方式也像私人一样)
添加(do
之前)
idum = - omp_get_thread_num()
为不同的线程提供不同的随机数
删除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),条件的使用可能会改变使用更有效的策略。