我们有一个大型的Fortran/MPI代码库,利用节点上的System-V共享内存段。我们在拥有32个处理器的fat节点上运行,但只有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编写了这个东西,所以我们的数组是连续的;我想访问也是连续的。
你有什么想法吗?
谢谢, Joly
以下是我正在进行的操作的伪代码模板。没有代码作为参考,所以希望我没有忘记任何重要的事情,但当我回到办公室时,我会确保的...
问题出现在我们决定切换到异步通信时。由于节点上只有几个CPU通过MPI进行通信,但所有CPU都可以看到接收到的数组(通过共享内存),因此除非我们执行某种形式的障碍,否则CPU不知道通信CPU何时完成,那么为什么要使用异步通信呢?
理想的、假设的解决方案是将请求标签放在SMP段中,并在需要知道的CPU上运行mpi_request_get_status。当然,请求标签仅在通信CPU上注册,因此它不起作用!另一个提出的可能性是在通信线程上分支一个线程,并在其中使用循环运行mpi_request_get_status,标志参数在共享内存段中,以便所有其他图像都可以看到。不幸的是,这也不是一个选项,因为我们受到约束,不能使用线程库。
我们想到的唯一可行的选择似乎起作用,但感觉像是一个肮脏的黑客。我们在接收缓冲区的上限地址中放入了一个不可能的值,这样一旦mpi_irecv完成,该值就会改变,因此每个CPU都知道何时可以安全地使用缓冲区。这样做是否可以?如果MPI实现可以保证连续传输数据,则似乎只有它能够可靠地工作。由于我们用Fortran编写了这个东西,所以我们的数组是连续的;我想访问也是连续的。
你有什么想法吗?
谢谢, Joly
以下是我正在进行的操作的伪代码模板。没有代码作为参考,所以希望我没有忘记任何重要的事情,但当我回到办公室时,我会确保的...
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))”。