我们有一个大型的 Fortran/MPI 代码库,它利用节点上的 system-V 共享内存段。我们在具有 32 个处理器的胖节点上运行,但只有 2 或 4 个 NIC,每个 CPU 的内存相对较少;所以我们的想法是我们建立一个共享内存段,每个 CPU 在其上执行其计算(在其 SMP 数组的块中)。然后 MPI 用于处理节点间通信,但仅在 SMP 组中的主节点上。该过程是双缓冲的,对我们来说效果很好。
当我们决定切换到异步通信以隐藏一些延迟时,问题就出现了。由于节点上只有几个 CPU 通过 MPI 进行通信,但所有 CPU 都看到接收到的数组(通过共享内存),所以 CPU 不知道通信 CPU 何时完成,除非我们制定某种屏障,并且那为什么要异步通信呢?
理想的假设解决方案是将请求标签放在 SMP 段中,并在需要知道的 CPU 上运行 mpi_request_get_status。当然,请求标签只注册在通信CPU上,所以不起作用!另一个建议的可能性是在通信线程上分支一个线程并使用它在循环中运行 mpi_request_get_status,在共享内存段中使用标志参数,以便所有其他图像都可以看到。不幸的是,这也不是一个选择,因为我们被限制不使用线程库。
我们提出的唯一可行的选择似乎可行,但感觉就像一个肮脏的黑客。我们在接收缓冲区的上限地址中放置了一个不可能的值,这样一旦 mpi_irecv 完成,该值就会改变,因此每个 CPU 都知道何时可以安全地使用缓冲区。那样可以么?似乎只有在 MPI 实现可以保证连续传输数据的情况下,它才能可靠地工作。这听起来几乎令人信服,因为我们已经用 Fortran 编写了这个东西,所以我们的数组是连续的;我想访问也将是。
有什么想法吗?
谢谢,乔利
这是我正在做的那种事情的伪代码模板。家里没有代码作为参考,所以我希望我没有忘记任何重要的东西,但我会确保当我回到办公室时......
pseudo(array_arg1(:,:), array_arg2(:,:)...)
integer, parameter : num_buffers=2
Complex64bit, smp : buffer(:,:,num_buffers)
integer : prev_node, next_node
integer : send_tag(num_buffers), recv_tag(num_buffers)
integer : current, next
integer : num_nodes
boolean : do_comms
boolean, smp : safe(num_buffers)
boolean, smp : calc_complete(num_cores_on_node,num_buffers)
allocate_arrays(...)
work_out_neighbours(prev_node,next_node)
am_i_a_slave(do_comms)
setup_ipc(buffer,...)
setup_ipc(safe,...)
setup_ipc(calc_complete,...)
current = 1
next = mod(current,num_buffers)+1
safe=true
calc_complete=false
work_out_num_nodes_in_ring(num_nodes)
do i=1,num_nodes
if(do_comms)
check_all_tags_and_set_safe_flags(send_tag, recv_tag, safe) # just in case anything else has finished.
check_tags_and_wait_if_need_be(current, send_tag, recv_tag)
safe(current)=true
else
wait_until_true(safe(current))
end if
calc_complete(my_rank,current)=false
calc_complete(my_rank,current)=calculate_stuff(array_arg1,array_arg2..., buffer(current), bounds_on_process)
if(not calc_complete(my_rank,current)) error("fail!")
if(do_comms)
check_all_tags_and_set_safe(send_tag, recv_tag, safe)
check_tags_and_wait_if_need_be(next, send_tag, recv_tag)
recv(prev_node, buffer(next), recv_tag(next))
safe(next)=false
wait_until_true(all(calc_complete(:,current)))
check_tags_and_wait_if_need_be(current, send_tag, recv_tag)
send(next_node, buffer(current), send_tag(current))
safe(current)=false
end if
work_out_new_bounds()
current=next
next=mod(next,num_buffers)+1
end do
end pseudo
因此,理想情况下,我希望在通信进程的另一个线程中的循环中运行“check_all_tags_and_set_safe_flags”,或者更好:取消“安全标志”并使发送/接收的句柄在从站上可用,然后我可以在计算从站之前运行:“check_tags_and_wait_if_need_be(current, send_tag, recv_tag)”(mpi_wait),而不是“wait_until_true(safe(current))”。