Parallel processing 是否可以删除以下内容$OMP临界区
我有一个fortran代码,由于一些$OMP关键区域,显示了一些非常不令人满意的性能。这个问题实际上更多的是关于如何避免关键区域,以及是否可以删除这些区域?在这些关键区域中,我正在更新计数器并将值读/写到数组中Parallel processing 是否可以删除以下内容$OMP临界区,parallel-processing,fortran,openmp,Parallel Processing,Fortran,Openmp,我有一个fortran代码,由于一些$OMP关键区域,显示了一些非常不令人满意的性能。这个问题实际上更多的是关于如何避免关键区域,以及是否可以删除这些区域?在这些关键区域中,我正在更新计数器并将值读/写到数组中 i=0 j=MAX/2 total = 0 !$OMP PARALLEL PRIVATE(x,N) MAIN_LOOP:do $OMP CRITICAL total = total + 1 x = arr
i=0
j=MAX/2
total = 0
!$OMP PARALLEL PRIVATE(x,N)
MAIN_LOOP:do
$OMP CRITICAL
total = total + 1
x = array(i)
i = i + 1
if ( i > MAX) i=1 ! if the counter is past the end start form the beginning
$OMP END CRITICAL
if (total > MAX_TOTAL) exit
! do some calculations here and get the value of the integer (N)
! store (N) copies of x it back in the original array with some offset
!$OMP CRITICAL
do p=1,N
array(j)=x
j=j+1
if (j>MAX) j=1
end do
!$OMP END CRITICAL
end do MAIN_LOOP
$OMP END PARALLEL
我想到的一件简单的事情是通过使用显式动态循环调度消除total
上的计数器
!$OMP PARALLEL DO SCHEDULE(DYNAMIC)
MAIN_LOOP:do total = 1,MAX_TOTAL
! do the calculation here
end do MAIN_LOOP
!$OMP END PARALLEL DO
我还考虑将数组的不同部分分配给每个线程,并使用线程ID
进行偏移。这一次,每个处理器都有自己的计数器,这些计数器将存储在数组count\u i(ID)
和类似的东西中
!this time the size if array is NUM_OMP_THREADS*MAX
x=array(ID + sum(count_i)) ! get the offset by summing up all values
ID=omp_get_thread_num()
count_i(ID)=count_i(ID)+1
if (count_i(ID) > MAX) count_i(ID) = 1
但是,这将扰乱顺序,并且不会像原始方法那样进行操作。此外,由于不同的线程无法适应整个范围1:MAX
我将感谢你的帮助和想法 您对关键部分的使用在这里有点奇怪。使用临界区的动机必须是避免数组中的条目在读取之前被破坏。您的代码确实通过充当屏障来实现这一点,但这只是偶然的。尝试用OMP屏障替换关键的东西,你仍然应该得到正确的结果和同样可怕的速度
由于总是在距离写入位置有一半长度的地方写入数组,因此可以通过将操作分为一个步骤来避免关键部分,该步骤从前半部分读取,然后写入后半部分,反之亦然。(编辑:编辑问题后,这不再正确,因此下面的方法将不起作用)
这里f(x)
表示要对数组值进行的任何计算。如果您不希望它成为函数,那么它不必是函数。如果不清楚,这段代码首先并行地遍历数组前半部分中的条目。第一个任务可能经过i=1,1+nproc、1+2*nproc等,而第二个任务经过i=2,2+nproc、2+2*nproc等。这可以在没有任何锁定的情况下并行完成,因为在此循环中读取和写入的数组部分之间没有重叠。第二个循环仅在每个任务完成第一个循环后启动,因此循环之间没有碰撞
与您的代码不同,这里每个线程有一个i
,因此不需要锁定来更新它(循环变量是自动私有的)
这假设您只想通过阵列一次。否则,您可以在这两个循环上循环:
do iouter = 1, (max_total+size(array)-1)/size(array)
nleft = max_total-(iouter-1)*size(array)
nhalf = size(array)/2
!$omp parallel do
do i = 1, min(nhalf,nleft)
array(i+nhalf) = f(array(i))
end do
!$omp parallel do
do i = 1, min(nhalf,nleft-nhalf)
array(i) = f(array(i+nhalf))
end do
end do
编辑:您的新示例令人困惑。我不知道它应该做什么。根据N
的值,数组值可能会在使用前被删除。这是故意的吗?当你不清楚自己想做什么时,很难回答你的问题/ 我想了一会儿,我的感觉是这个具体问题没有好的答案
事实上,乍一看,您的代码似乎是解决上述问题的好方法(尽管我个人觉得问题本身有点奇怪)。但是,您的实施中存在以下问题:
- 如果由于某种原因,其中一个线程在处理其迭代时被延迟,会发生什么情况?想象一下,拥有第一个索引的线程需要一段时间来处理它(例如,延迟了某个第三方进程,占用了线程被固定/调度的核心上的CPU时间),并且是最后一个完成的线程。。。然后,它将以与顺序算法完全不同的顺序将其值设置回
array
。这是你可以接受的算法吗
- 即使没有这种“极端”延迟,您能接受
i
索引在线程之间的分布顺序与j
索引随后更新的顺序不同吗?如果拥有i+1
的线程在拥有i
的线程之前完成,它将使用索引j
而不是索引j+n
同样,我不确定我是否理解您的算法的所有微妙之处,以及错过迭代排序的弹性有多大,但如果排序很重要,那么这种方法是错误的。在这种情况下,我想适当的并行化可以是这样的(放入子例程以使其可编译):
我希望这能奏效。但是,除非检索到n
的循环的中心部分进行了认真的计算,否则这不会比顺序版本快。我不知道您可以在OpenMP区域内调用exit…这里的MAX\u TOTAL
是什么,它与大小(数组)相比如何
和MAX
?MAX\u TOTAL
是循环MAIN\u循环:do
应该执行的迭代次数。这与do total=1,MAX_total
Alexander Cska是一样的:那么,代码可能会在数组中进行多次或多次传递?我真的不确定您的代码实际执行的是您想要的还是您认为它执行的:在您的代码片段中,x
由所有线程按顺序共享和更新(无特定顺序的一个接一个).所以你不知道它有什么值,但你用它来计算,知道它可以在你背后的任何时刻发生变化,当另一个线程到达临界
部分…这是什么意思?这不起作用,因为主循环和数组偏移索引不是线性链接的。我正在生成许多I
和j
值在主循环的一个周期内。关键区域是,没有两个线程在
do iouter = 1, (max_total+size(array)-1)/size(array)
nleft = max_total-(iouter-1)*size(array)
nhalf = size(array)/2
!$omp parallel do
do i = 1, min(nhalf,nleft)
array(i+nhalf) = f(array(i))
end do
!$omp parallel do
do i = 1, min(nhalf,nleft-nhalf)
array(i) = f(array(i+nhalf))
end do
end do
subroutine loop(array, maxi, max_iteration)
implicit none
integer, intent(in) :: maxi, max_iteration
real, intent(inout) :: array(maxi)
real :: x
integer :: iteration, i, j, n, p
i = 0
j = maxi/2
!$omp parallel do ordered private(x, n, p) schedule(static,1)
do iteration = 1,max_iteration
!$omp ordered
x = array(wrap_around(i, maxi))
!$omp end ordered
! do some calculations here and get the value of the integer (n)
!$omp ordered
do p = 1,n
array(wrap_around(j, maxi)) = x
end do
!$omp end ordered
end do
!$omp end parallel do
contains
integer function wrap_around(i, maxi)
implicit none
integer, intent(in) :: maxi
integer, intent(inout) :: i
i = i+1
if (i > maxi) i = 1
wrap_around = i
end function wrap_around
end subroutine loop