mpi4py 中的通用化请求

在上一篇中我们介绍了 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 及其它参数 argskargs 创建并注册一个通用化请求,返回一个 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。可通过参数 argskargs 传递额外信息。

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 仅执行一次。可通过参数 argskargs 传递额外信息。

cancel_fn(bool completed, *args, **kargs)

由 MPI.Grequest.Cancel 触发执行。如果 cancel 执行时已经完成了 MPI.Grequest.Complete 则 MPI 会向 cancel_fn 传递 completed = True,否则传递 completed = False。可通过参数 argskargs 传递额外信息。

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 中的数据类型解析。

你可能感兴趣的:(mpi4py 中的通用化请求)