如何在OpenMP中使用锁

时间:2014-08-16 22:57:19

标签: parallel-processing locking fortran openmp fortran90

我是OpenMP的新手,发现它有点难以理解OpenMP中的锁是如何工作的。下面是使用Fortran 90编写的用于执行LU分解的示例代码。任何人都可以解释锁在这段代码中的工作原理

 program lu

  implicit none
  integer, parameter :: DP=kind(0.0D0),n=20


!--  Variables
  integer :: i,j,k,nthr,thrid,chunk=1
  real(kind=DP), dimension(:,:),allocatable :: A,B,L,U
  real(kind=DP) :: timer,error,walltime
  integer(kind=8), dimension(n)::lck
  integer::omp_get_thread_num,omp_get_max_threads
  nthr=omp_get_max_threads()
  allocate(A(n,n))
  allocate(B(n,n))
  allocate(L(n,n))
  allocate(U(n,n))



!-- Set up locks for each column
  do i=1,n
     call omp_init_lock(lck(i))
  end do

  timer=walltime()
!$OMP PARALLEL PRIVATE(i,j,k,thrid) 
  thrid=omp_get_thread_num();

!-- Initiate matrix
!$OMP DO SCHEDULE(STATIC,chunk)
  do j=1,n
     do i=1,n
        A(i,j)=1.0/(i+j)
     end do
     call omp_set_lock(lck(j))
  end do
!$OMP END DO

!-- First column of L
  if (thrid==0) then
     do i=2,n
        A(i,1)=A(i,1)/A(1,1)
     end do
     call omp_unset_lock(lck(1))     
  end if

!-- LU-factorization
  do k=1,n
      call omp_set_lock(lck(k))
     call omp_unset_lock(lck(k))
!$OMP DO SCHEDULE(STATIC,chunk)
     do j=1,n
        if (j>k) then 
           do i=k+1,n
              A(i,j)=A(i,j)-A(i,k)*A(k,j)
           end do
           if (j==k+1) then
              do i=k+2,n
                 A(i,k+1)=A(i,k+1)/A(k+1,k+1)
              end do
              call omp_unset_lock(lck(k+1))
           end if
        end if
     end do
!$OMP END DO NOWAIT
  end do

!$OMP END PARALLEL
  timer=walltime()-timer

  write(*,*) 'n = ',n,'   time = ',timer,'   nthr = ',nthr

! CHECK CORRECTNESS
  do j=1,n
     L(j,j)=1
     U(j,j)=A(j,j)
     do i=j+1,n
        L(i,j)=A(i,j)
        U(i,j)=0
     end do
     do i=1,j-1
        U(i,j)=A(i,j)
        L(i,j)=0
     end do
  end do
  B=0
  do j=1,n
     do k=1,n
        do i=1,n
           B(i,j)=B(i,j)+L(i,k)*U(k,j)
        end do
     end do
  end do
  error=0.0
  do j=1,n
     do i=1,n
        error=error+abs(1.0/(i+j)-B(i,j))
     end do
  end do

  write(*,*) 'ERROR: ',error

end program lu

下面列出了另一个包含walltime函数的文件。它应该与主文件一起编译。

function walltime()
     integer, parameter:: DP = kind(0.0D0)
     real(DP) walltime
     integer::count,count_rate,count_max
     call system_clock(count,count_rate,count_max)    
     walltime=real(count,DP)/real(count_rate,DP)
end function walltime

1 个答案:

答案 0 :(得分:2)

免责声明:我没有使用锁定机制的经验,并查看了标准,以了解这将如何工作。我可能错了......


首先,您的代码存在一些问题:此代码无法使用最新版本的gfortran进行编译。您必须将函数walltime移动到程序的contains部分,并且应使用USE omp_lib来定义所有必需的函数(并删除生成的重复定义)。此外,您必须以标准方式定义锁:

integer(kind=OMP_LOCK_KIND), dimension(n) :: lck

现在回答您的问题:对OMP_INIT_LOCK的调用会将您的lck数组初始化为解锁状态。所有线程都将获得此变量的副本。然后启动并行部分。

在第一个循环中,数组被初始化为类似于 Hilbert矩阵的东西,并且每个锁被设置。 第二个块仅由第一个线程执行,第一个锁定被释放。仍然没有什么有趣所有线程都输入以下循环,并且所有线程都在等待k次锁定,因为omp_set_lock等待,直到获得锁定。以下omp_unset_lock允许所有其他线程跟随。由于已经释放的第一个锁定,所有线程将立即进入内部循环,最后一个线程将释放下一个锁定。到那时,这个线程释放了这个锁,其他线程可能已经在等待这个锁了。

原则上,此算法提供某种形式的同步,以确保在输入时已计算k+1 - 循环所需的数据。