Fortran OpenMP代码中伸缩性差和分段错误

时间:2019-03-11 16:01:37

标签: fortran segmentation-fault openmp

以并行方式执行程序时遇到麻烦。这是测试代码。

module test
use, intrinsic :: iso_fortran_env, only: dp => real64
implicit none
contains

subroutine Addition(x,y,s)
    real(dp),intent(in) :: x,y
    real(dp), intent(out) :: s
    s = x+y
end subroutine Addition

function linspace(length,xi,xf) result (vec)
! function to create an equally spaced vector given a begin and end point
    real(dp),intent(in) :: xi,xf
    integer, intent(in) :: length
    real(dp),dimension(1:length) :: vec
    integer ::i
    real(dp) :: increment

    increment = (xf-xi)/(real(length)-1)
    vec(1) = xi
    do i = 2,length
        vec(i) = vec(i-1) + increment
    end do
end function linspace
end module test

program paralleltest
use, intrinsic :: iso_fortran_env, only: dp => real64
use test
use :: omp_lib
implicit none
integer, parameter :: length = 1000
real(dp),dimension(length) :: x,y
real(dp) :: s
integer:: i,j
integer :: num_threads = 8
real(dp),dimension(length,length) :: SMatrix

 x = linspace(length,.0d0,1.0d0)
 y = linspace(length,2.0d0,3.0d0)

!$ call omp_set_num_threads(num_threads)
!$OMP PARALLEL DO
do i=1,size(x)
    do j = 1,size(y)
    call Addition(x(i),y(j),s)
    SMatrix(i,j) = s
    end do
end do
!$OMP END PARALLEL DO

open(unit=1,file ='Add6.dat')
do i= 1,size(x)
    do j= 1,size(y)
        write(1,*) x(i),";",y(j),";",SMatrix(i,j)
    end do
end do
close(unit=1)
end program paralleltest

我以以下方式运行程序gfortran-8 -fopenmp paralleltest.f03 -o pt.out -mcmodel=medium,然后以export OMP_NUM_THREADS=8运行 这个简单的代码给我带来了至少两个关于并行处理的大问题。首先是,如果我使用length = 1100或更高版本运行,则出现Segmentation fault (core dump)错误消息,但是值较小时,它运行就没有问题。第二个是花费的时间。当我使用length = 1000(使用time ./pt.out运行)时,花费的时间是1,732s,但是如果我以顺序方式运行(无需调用-fopenmp库和taskset -c 4 time./pt.out)需要1,714s。我猜这两种方式之间的区别出现在更长,更复杂的代码中,而并行更有用。实际上,当我尝试使用与8个线程并行运行的更复杂的计算来进行测试时,时间减少了一半,比顺序进行的时间减少了一半,但没有我预期的八分之一。鉴于此,我的问题是,是否始终可以进行任何优化,还是取决于代码?其次,是否有一种友好的方法来控制哪个线程运行哪个迭代?那是第一次运行第一次length/8迭代,依此类推,就像用不同的代码执行几个taskset一样,其中每个都是我想要的迭代。

1 个答案:

答案 0 :(得分:3)

正如我所评论的,分段错误已在其他Why Segmentation fault is happening in this openmp code?处理,我将使用可分配的数组,但您也可以使用ulimit -s设置堆栈大小。

关于时间,几乎所有的运行时都花费在将数组写入外部文件中。

但是,即使您删除了该错误,并且仅使用omp_get_wtime()来测量在并行部分中花费的时间并增加了问题的大小,它仍然无法很好地扩展。这是因为CPU需要执行的计算量很少,并且大量数组写入内存(访问主内存的速度很慢-高速缓存未命中)。

正如Jean-Claude Arbaut所指出的那样,您的循环顺序是错误的,并且使访问内存的速度更加缓慢。某些编译器可以以更高的优化级别(-O2-O3)为您更改此设置,但其中只有一部分。

更糟糕的是,正如吉姆·考尼(Jim Cownie)所指出的,您有一个race condition。多个线程尝试使用相同的s进行读写,因此程序无效。您需要使用sprivate(s)设为私有。


使用上述修复程序,我得到了具有四个内核和四个线程的并行段,速度大约快了两倍。不要尝试使用超线程,它会使程序变慢。

如果您给CPU做更多的计算工作,例如s = Bessel_J0(x)/Bessel_J1(y),它对我来说伸缩性很好,四个线程的速度几乎快了四倍,而超线程确实加快了速度。


最后,我建议仅删除线程数量的手动设置,这是测试的难题。如果将其删除,则可以轻松使用OMP_NUM_THREADS=4 ./a.out