首页 > 解决方案 > 了解 mpi4py 中的 Put/Get

问题描述

我正在学习如何在 mpi4py 中使用单向通信。为了测试我的理解,我想出了这个人为的例子:

import mpi4py.MPI as mpi
import numpy as np
import time

def main():
    rank = mpi.COMM_WORLD.Get_rank()
    n_proc = mpi.COMM_WORLD.Get_size()

    assert(n_proc == 2)

    buff = np.zeros(10, dtype='d')
    win = mpi.Win.Create(buff, 1, mpi.INFO_NULL, mpi.COMM_WORLD)

    if (rank == 0):
        win.Lock(1)
        win.Put([buff, mpi.DOUBLE], 1)
        win.Unlock(1)

        buff[-1] = 9
        time.sleep(30)

        win.Lock(1)
        win.Put([buff, mpi.DOUBLE], 1)
        win.Unlock(1)

    else:
        holder = np.zeros(10)
        failures = 0
        while (holder[-1] != 9):
            failures += 1
            win.Lock(0)
            win.Get([holder, mpi.DOUBLE], 0)
            win.Unlock(0)
        print('Took', failures, 'dials')

if __name__=='__main__':
    main()

我希望它按如下方式工作:排名 0 将等待 30 秒,然后将包含的 ndarray[0, 0, ..., 0, 9]放入共享内存。同时,rank 1 将不断检查 rank 0 是否更新了它的共享内存——一旦更新,它将打印出它看到的内容。具体来说,我希望 rank 1 会在 30 秒等待期间多次执行其 while 循环,而 rank 0 空闲。但是,当我使用 运行此代码时mpirun -n 2 python mwe.py,我始终得到以下输出:

Took 2 dials

表明调用Get只发生了两次。

具体问题:为什么只有两个Get从 rank 1 调用?

更模糊的问题:我在这个 MWE 中是否犯了明显的错误?一般来说,我对 mpi4py/MPI 很陌生。

标签: python-3.xparallel-processingmpimpi4py

解决方案


如您所说,您正在创建一个共享内存窗口。该窗口授予通信器中的其他 MPI 进程直接访问存储您的确切内存的权限buff。当您更改时,buff[-1] = 9这当然会存储在所述内存中,并且窗口会立即受到影响。因此,Put对本地进程(从 rank 0 到 rank 0)的显式调用是不必要的,并且Put仅对将数据放置在远程内存窗口中有用。buff其他 MPI 进程可以在排名 0 甚至开始其 30 秒的睡眠操作之前从窗口中读取您更改的变量。

不幸的是,您看到“Took 2 dials”的原因是因为 0 级和 1 级可能同时执行它们的第一个锁定和解锁,然后 0 级执行buff[-1] = 9然后循环在 1 级上重复,它读取更新的缓冲区并退出2次迭代后。

PS:为了测试,先放个1s sleepbuff[-1] = 9


推荐阅读