在上一篇中我们介绍了如何安装和使用 mpi4py,下面我们以几个简单的例子来展示怎么使用 mpi4py 来进行并行编程,以使读者能够快速地上手使用 mpi4py。这些例子来自 mpi4py 的 Document,有些做了一些适当的改动。
点到点通信
传递通用的 Python 对象(阻塞方式)
这种方式非常简单易用,适用于任何可被 pickle 系列化的 Python 对象,但是在发送和接收端的 pickle 和 unpickle 操作却并不高效,特别是在传递大量的数据时。另外阻塞式的通信在消息传递时会阻塞进程的执行。
# p2p_blocking.py
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
if rank == 0:
data = {
'a': 7, 'b': 3.14}
print 'process %d sends %s' % (rank, data)
comm.send(data, dest=1, tag=11)
elif rank == 1:
data = comm.recv(source=0, tag=11)
print 'process %d receives %s' % (rank, data)
运行结果如下:
$ mpiexec -n 2 python p2p_blocking.py
process 0 sends {'a': 7, 'b': 3.14}
process 1 receives {'a': 7, 'b': 3.14}
传递通用的 Python 对象(非阻塞方式)
这种方式非常简单易用,适用于任何可被 pickle 系列化的 Python 对象,但是在发送和接收端的 pickle 和 unpickle 操作却并不高效,特别是在传递大量的数据时。非阻塞式的通信可以将通信和计算进行重叠从而大大改善性能。
# p2p_non_blocking.py
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
if rank == 0:
data = {
'a': 7, 'b': 3.14}
print 'process %d sends %s' % (rank, data)
req = comm.isend(data, dest=1, tag=11)
req.wait()
elif rank == 1:
req = comm.irecv(source=0, tag=11)
data = req.wait()
print 'process %d receives %s' % (rank, data)
运行结果如下:
$ mpiexec -n 2 python p2p_non_blocking.py
process 0 sends {'a': 7, 'b': 3.14}
process 1 receives {'a': 7, 'b': 3.14}
传递 numpy 数组(高效快速的方式)
对类似于数组这样的数据,准确来说是具有单段缓冲区接口(single-segment buffer interface)的 Python 对象,如 numpy 数组及内置的 bytes/string/array 等,可以用一种更为高效的方式直接进行传递,而不需要经过 pickle 系列化和恢复。以这种方式传递数据需要使用通信子对象的以大写字母开头的方法