我一直爱上了Fortran的Coarrays框架的易用性,因为与MPI等较低级别的API相比,它是多么干净。
但是我无法梳理出的一件事是,是否有一种方法可以知道如何明确地告诉Fortran异步执行放置和获取。这样做的好处是复制MPI的MPI_I*
调用,这允许重叠的通信和计算。
我对重叠感兴趣的原因是出于性能原因。我想到的特定应用是使用粒子方法的CFD,其中域被细分,光环粒子在每个时间步进行交换。使用我目前更熟悉的MPI p2p调用,我正在启动进程之间的粒子信息交换,然后在通信完成时执行计算,类似于:
do pid = 0, numprocs-1
if (pid /= procid) then
! post sends
call MPI_ISEND(neighbours(pid+1)%sendbuff, &
neighbours(pid+1)%n_send, &
particle_derived_type, &
pid, &
0, &
MPI_COMM_WORLD, &
request(pid+1), &
ierr)
! post receives
call MPI_IRECV(neighbours(pid+1)%recvbuff, &
neighbours(pid+1)%n_recv, &
particle_derived_type, &
pid+1, &
0, &
MPI_COMM_WORLD, &
request(numprocs+pid+1), &
ierr)
end if
end do
! do some heavy computation
call MPI_WAITALL(2*numprocs, request, status, ierr)
这只是为了演示。实际上,每个进程只会与其相邻进程而不是所有进程交流信息。在这里使用MPI_ISEND/RECV
的好处是我不必担心锁定,并且可以在发送和接收完成时进行一些计算。
一种使用 Coarray 的等效示例:
do pid = 1, numprocs
if (i /= this_image()) then
! put data into remote neighbour images
n_send = neighbours(pid)%n_send
neighbours(this_image())[pid]%recv_buff(1:n_send) = neighbours(pid)%send_buff(1:n_send)
end if
end do
! do some heavy computation
sync all
这很酷,因为它更紧凑。但我不确定"看跌期权"是否会像MPI_ISEND/RECV
一样在开始转移后返回.
因此,对于此示例,我对复制MPI_I*
在Fortran Coarrays中将通信与计算重叠的能力感兴趣,因为它对于优化CFD仿真的性能非常重要。
编辑希望更清楚地解释为什么我想将通信与 comps 重叠。
协阵列通信模型是远程内存访问/单侧通信之一,而不是点对点通信模型。
在赋值语句中
integer i
i = 3
print *, i
end program
i
设置为3
,"立即"。print 语句中的引用以类似的方式发生。
人们不会质疑"放置"和"获取"是同步还是异步阻塞。
现在考虑
integer, volatile :: i
i = 3
print *, i
end program
在第一个示例中,处理器可以决定不存储/读取值为i
的永久内存位置。在第二个示例中,必须获取而不是假定i
的值。
当涉及多个图像时,我们会看到相似的图像:1
integer i[*]
if (this_image()==1) then
i = 1
i[2] = 3
end if
sync all
print *, i
end program
此处,图像1 有两个分配,分别设置两个图像上的i
值。两者都"立即"发生。
一旦执行i=1
,第一个图像上的i
值就会1
。一旦执行i[2]=3
,第二个图像上的i
值就会3
。
现在,第二个作业中的"阻塞"(特别是)归结为完成作业意味着什么。
可能会进行两种极端的对话:
图1:嘿,图2,你在那里吗?
图2:苏普?
图 1:我想将您的
i
值设置为等于3
。图2:我完成手头的工作后会马上开始。
图1:不用担心,我会在你做的时候喝杯咖啡。
。时间流逝
图2:沃查,图1。
图片1:你好?
图2:我已经做了你想做的事。我的
i
现在等于3.
图片1:太好了,谢谢。我会回去工作。
可以与
图1:图 2,您的
i
值现在3
。
Fortran标准没有说明哪些对话发生了,但它为第二个对话保持开放。第二次对话甚至不必在分配语句发生前后(Fortran 2018,11.6.2 注释 4):2
在实践中,处理器可以复制非易失性 图像上的共阵列 [..],并且作为优化,推迟复制更改的 值返回到仍在使用的永久内存位置。由于变量不是易失性的,因此推迟此传输是安全的 [..]
这就是说,任务可能会以某种方式阻塞,但没有要求或激励。重要的是,即使所有映像都尝试分配给所有其他映像上的协同阵列,分配中也不会发生死锁。
像这样的单方面沟通通过限制沟通伙伴之间的互动来发挥作用。
松散地说,如果一个映像设置了(非易失性)协阵列的值,则在发生某种同步之前,不允许其他映像定义或引用该协阵列。3设置值的映像可以确保,在此同步之前,协阵列与该映像所决定的完全一致。
通信可以恰好在赋值时发生,也可以在以后的某个时间发生;计算可能会停止,直到值被传输、甚至确认或立即继续。Fortran没有告诉我们这一点,但处理器被允许推迟甚至消除通信,或重叠通信和计算。编译器供应商通常热衷于获得最佳行为。
该计划
integer i[*]
i[2] = this_image()
print *, i[2]
end program
无效。使此类程序无效的限制允许一系列实现方法。作为一名程序员,你对使用哪种实现几乎没有发言权或知识。
我只字未提原子行动(包括事件)或集体行动。
1以下示例假定有两个图像。
2如果图像 1 对i[2]
进行了多次分配,则图像可能会选择在一次对话中只为图像 2 提供最终值。事实上,在某些情况下可以完全避免第二次对话。
3正是这个限制允许我们说协数组的值会立即受到影响:一个有效的 Fortran 程序不能对值应该是什么有冲突。