在上一篇中我们介绍了 mpi4py 中的 Status 对象,下面我们将介绍 mpi4py 中的通用化请求。
MPI-2 通过通用请求对象为应用程序提供自定义非阻塞操作的机制。自定义的非阻塞操作也会返回 request 对象,可以使用 Test,Wait 及它们的变种来测试和等待其完成。通用请求(generalized requests)的非阻塞操作能够与程序的正常执行流程并发执行。与其它的 MPI 操作不同,通用请求相关联的操作是由应用程序而非 MPI 环境负责执行,因此必须有一种机制使得应用程序在执行完相关操作后能够通知 MPI 环境。为此 MPI 提供了 MPI.Grequest.Complete 函数来实现这种“告知”,在此机制下,MPI 只负责保留通用请求操作的完成状态,其它状态信息则需应用程序自身负责维护。
mpi4py 中,通用化请求操作通过 MPI.Grequest 对象来完成。MPI.Grequest 类是 MPI.Request 类的子类,故 Wait 和 Test 等方法都可使用。
下面是通用化请求创建及完成相关方法的接口:
MPI.Grequest.Start(type cls, query_fn, free_fn, cancel_fn, args=None, kargs=None)
由查询回调函数 query_fn
,释放回调函数 free_fn
,取消回调函数 cancel_fn
及其它参数 args
和 kargs
创建并注册一个通用化请求,返回一个 MPI.Grequest 对象。
其中回调函数的接口如下:
query_fn(Status status, *args, **kargs)
query_fn 负责设置操作成功/失败/取消等通用请求的状态信息 status
。仅在 MPI.Grequest.Complete 返回后才可能会被执行到。Wait,Wait_any,Wait_some,Wait_all;Test,Test_any,Test_some,Test_all;以及 MPI.Grequest.Get_status 等都要调用 query_fn。可通过参数 args
可 kargs
传递额外信息。
free_fn(*args, **kargs)
该函数在释放通用请求对象时调用,用于清除应用程序申请的内存。要在 query_fn 之后执行,由 Wait,Wait_any,Wait_some,Wait_all;Test,Test_any,Test_some,Test_all;以及 MPI.Grequest.Free 等调用触发执行。正常的程序中 free_fn 仅执行一次。可通过参数 args
可 kargs
传递额外信息。
cancel_fn(bool completed, *args, **kargs)
由 MPI.Grequest.Cancel 触发执行。如果 cancel 执行时已经完成了 MPI.Grequest.Complete 则 MPI 会向 cancel_fn 传递 completed
= True,否则传递 completed
= False。可通过参数 args
可 kargs
传递额外信息。
MPI.Grequest.Complete(self)
用于应用程序通知 MPI 环境其发起的通用请求操作已经完成,此时 MPI.Grequest.Wait 系列调用会返回,而 MPI.Grequest.Test 系列调用会返回 True。
例程
下面给出使用例程。
# greq.py
"""
Demonstrates the usage of generalized request.
Run this with 1 processes like:
$ mpiexec -n 1 python greq.py
or
$ python greq.py
"""
import time
import threading
from mpi4py import MPI
comm = MPI.COMM_WORLD
def query_fn(status, *args, **kargs):
print 'Call query_fn with args = %s, kargs = %s...' % (args, kargs)
status.source = MPI.UNDEFINED
status.tag = MPI.UNDEFINED
status.cancelled = False
status.Set_elements(MPI.BYTE, 0)
return MPI.SUCCESS
def free_fn(*args, **kargs):
print 'Call free_fn with args = %s, kargs = %s...' % (args, kargs)
if 'a' in kargs:
# change the kargs argument (have effect only for changeable type like list, dict, etc)
print "Append 3 to kargs['a']"
kargs['a'].append(3)
return MPI.SUCCESS
def cancel_fn(completed, *args, **kargs):
print 'Call cancel_fn with completed = %s, args = %s, kargs = %s...' % (completed, args, kargs)
return MPI.SUCCESS
# define an user-defined non-blocking operate
def iop(*args, **kargs):
def compute(greq):
# sleep 1 second to simulate a compute-intensive task
time.sleep(1.0)
# call Complete method to inform MPI implementation that
# the operation associated with this greq has completed
greq.Complete()
# create a generalized request
greq = MPI.Grequest.Start(query_fn, free_fn, cancel_fn, args=args, kargs=kargs)
# call compute in a separate thread, so it will not block the return of this
iop_thread = threading.Thread(target=compute, name='iop_thread', args=(greq,))
iop_thread.daemon = True
# start the thread
iop_thread.start()
return greq
a = []
print 'Before the cal of iop, a = %s' % a
# call the user-defined non-blocking operation,
# which will return a MPI.Grequest object immediately
greq = iop(1, 2, a=a)
# test if the non-blocking operate is completed
status = MPI.Status()
print 'Is complete: %s' % greq.Test(status)
print 'source = %d, tag = %d, cancelled = %s, count = %d' % (status.source, status.tag, status.cancelled, status.count)
# call Cancel
greq.Cancel()
print 'Is complete: %s' % greq.Test()
# wait 1 second for the complete of iop
time.sleep(1.0)
# call Cancel
greq.Cancel()
print 'Is complete: %s' % greq.Test(status)
print 'source = %d, tag = %d, cancelled = %s, count = %d' % (status.source, status.tag, status.cancelled, status.count)
try:
# call Cancel after the complete of iop, wich will throw an exception
greq.Cancel()
except MPI.Exception as e:
print e.error_string
print 'After the complete of iop, a = %s' % a
运行结果如下:
$ python greq.py
Before the cal of iop, a = []
Is complete: False
source = -1, tag = -1, cancelled = False, count = 0
Call cancel_fn with completed = False, args = (1, 2), kargs = {'a': []}...
Is complete: False
Call cancel_fn with completed = True, args = (1, 2), kargs = {'a': []}...
Call query_fn with args = (1, 2), kargs = {'a': []}...
Call free_fn with args = (1, 2), kargs = {'a': []}...
Append 3 to kargs['a']
Is complete: True
source = -32766, tag = -32766, cancelled = False, count = 0
MPI_ERR_REQUEST: invalid request
After the complete of iop, a = [3]
以上介绍了 mpi4py 中的通用化请求,在下一篇中我们将介绍 mpi4py 中的数据类型解析。