在上一篇中我們簡要地介紹了 mpi4py 中的單邊通信概念,下面我們將介紹單邊通信的相關(guān)操作隙姿。
創(chuàng)建/釋放窗口對象
注意:在使用單邊通信操作之前,所有進程都須通過共同參與的創(chuàng)建窗口操作公開聲明自己可供訪問的內(nèi)存空間。
創(chuàng)建和釋放窗口對象的方法(MPI.Win 類方法)接口如下:
Create(type cls, memory, int disp_unit=1, Info info=INFO_NULL, Intracomm comm=COMM_SELF)
創(chuàng)建并返回用于單邊通信的窗口對象兄朋。在組內(nèi)通信子 comm
所指定的通信子范圍內(nèi)所有進程上執(zhí)行集合操作常拓,每個進程通過一塊內(nèi)存緩沖區(qū) memory
指定要創(chuàng)建的窗口渐溶,可以為 None(或 MPI.BOTTOM),此時不提供可供其它進程訪問的窗口弄抬。disp_unit
指定在遠端內(nèi)存訪問操作中的地址單位茎辐,即 origin 所指定的位置在 target 一側(cè)要以 target 進程所指定的 diap_unit
為單位計算。通常如果采用相同類型創(chuàng)建窗口掂恕,則統(tǒng)一將 disp_unit
設(shè)置成 1 即可拖陆。如果有的進程需要以組合數(shù)據(jù)類型(type)給出緩沖區(qū),則可能需要指定 disp_unit
為 sizeof(type)懊亡。info
對象用于為 MPI 環(huán)境提供優(yōu)化所需的輔助信息依啰,目前可用的 key 為 no_lock,如果該值設(shè)置為 True店枣,則指示 MPI 環(huán)境不對進程的本地窗口加鎖速警,也就是說此時表示應(yīng)用程序可以確信相應(yīng)窗口不會作為第三方參與通信叹誉,這樣就可減少相應(yīng)進程異步代理機制上的一些額外操作。參與創(chuàng)建窗口的進程可分別指定不同的 memory
闷旧,disp_unit
和 info
參數(shù)长豁,內(nèi)存中的同一塊區(qū)域也可同時存在于多個窗口中,只要應(yīng)用程序能確保并發(fā)訪問這塊區(qū)域所需的安全語義鸠匀。
Free(self)
釋放當(dāng)前窗口對象蕉斜。會在所有進程間實施 barrier 同步操作,直至所有進程都執(zhí)行完畢才返回缀棍。所有進程都必須在其遠端內(nèi)存訪問結(jié)束后才可調(diào)用此操作宅此。
通信操作
單邊通信的 3 種操作方法(MPI.Win 類方法)接口如下:
Put(self, origin, int target_rank, target=None)
該操作把當(dāng)前進程 origin
中的數(shù)據(jù)傳輸?shù)竭M程 target_rank
的 target
位置。參數(shù) origin
應(yīng)該是一個長度為2或3的 list 或 tuple爬范,類似于 [data, MPI.DOUBLE]
父腕,或者 [data, count, MPI.DOUBLE]
,以指明發(fā)送數(shù)據(jù)緩沖區(qū)青瀑,數(shù)據(jù)計數(shù)以及數(shù)據(jù)類型璧亮。當(dāng) count
省略時會利用 data
的字節(jié)長度和數(shù)據(jù)類型計算出對應(yīng)的 count
。對 numpy 數(shù)組斥难,其計數(shù)和數(shù)據(jù)類型可以自動推斷出來枝嘶,因此可以直接以 data
作為第一個參數(shù)傳給 origin
。target
可以是 None哑诊,一個整數(shù)或是一個長度為3的 list 或 tuple群扶,類似于 [target_disp, target_count, target_datatype]
,分別指定接收到的數(shù)據(jù)寫入 target_rank
進程相對于窗口內(nèi)存緩沖區(qū)的起始位置的偏移镀裤,數(shù)據(jù)量和數(shù)據(jù)類型竞阐,當(dāng)為 None 或一個整數(shù)時,會設(shè)置 target_disp
為 0 或該整數(shù)暑劝,而 target_count
和 target_datatype
會設(shè)置成目標(biāo)窗口緩沖區(qū)的大小和數(shù)據(jù)類型骆莹。與點到點通信類似,target 進程緩沖區(qū)的大小也需要有足夠的空間以容納要傳輸?shù)臄?shù)據(jù)担猛。如果 target_datatype
是自定義數(shù)據(jù)類型幕垦,則必須僅包含相對偏移,而不能使用絕對地址毁习,這一限制對后面的 Get 和 Accumulate 方法同樣適用智嚷。
此操作的實際效果相當(dāng)于執(zhí)行一次點到點通信,在源進程執(zhí)行 Send(origin, dest=target_rank, tag=tag)纺且,在目標(biāo)進程執(zhí)行 Recv(buf, source=source, tag=tag),不同的是稍浆,Put 操作的源進程指定所有通信參數(shù)载碌,而不需要在目標(biāo)進程里啟動與之匹配的接收操作猜嘱。
Get(self, origin, int target_rank, target=None)
該操作從 target_rank
的窗口緩沖區(qū)讀取數(shù)據(jù),各參數(shù)的含義及限制與 Put 基本相同嫁艇,只是數(shù)據(jù)傳輸?shù)姆较蚋臑橛赡繕?biāo)流向源朗伶。
Accumulate(self, origin, int target_rank, target=None, Op op=SUM)
該操作將 origin
中的數(shù)據(jù)用 Reduce 中所定義的操作 op
更新 target_rank
的窗口緩沖區(qū)中由 target
所指定位置處的數(shù)據(jù)。各參數(shù)的含義及限制與 Put 基本相同, op
指定操作的規(guī)約算符步咪。該操作只能使用 Reduce 內(nèi)置定義的操作和 Accumulate 增加的一個操作——MPI.REPLACE论皆。Put 是 Accumulate 執(zhí)行 MPI.REPLACE 的特殊情形』可以使用預(yù)定義數(shù)據(jù)類型和用戶自定義數(shù)據(jù)類型点晴,但 target_datatype
不能指定重疊的區(qū)域,且目標(biāo)進程中緩沖區(qū)不能超過目標(biāo)進程所聲明窗口的范圍悯周。
以上介紹的 Put粒督,Get,Accumulate 均為非阻塞操作禽翼,僅當(dāng)操作發(fā)起者對相同的窗口對象調(diào)用同步函數(shù)(在下一篇中將會介紹)后才能確保實際數(shù)據(jù)傳輸完畢屠橄。從啟動遠端內(nèi)存訪問操作起,到通過同步函數(shù)確認操作完成之間闰挡,不能更新本地進程為實現(xiàn)該次通信所使用的數(shù)據(jù)緩沖區(qū)锐墙,即使是 Get 操作,在此期間也不能更新其所使用的本地通信緩沖區(qū)长酗。
例程
下面給出單邊通信操作相關(guān)方法的使用例程溪北。
# win.py
"""
Demonstrates the usage of Create, Free, Put, Get, Accumulate.
Run this with 4 processes like:
$ mpiexec -n 4 python win.py
"""
import numpy as np
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
# Create and Put
if rank == 0:
mem = np.array([1, 2], dtype='i')
# create a window object with no accessable memory, used to communicate with other only
win = MPI.Win.Create(None, comm=comm)
# synchronize
win.Fence()
# put data into a memory window of rank 1
print 'rank 0 puts %s to rank 1' % mem
win.Put(mem, target_rank=1)
# synchronize
win.Fence()
else:
# initialize a memory of [0, 0]
mem = np.array([0, 0], dtype='i')
# use mem to create a window for receiving data
win = MPI.Win.Create(mem, comm=comm)
# synchronize
win.Fence()
# synchronize
win.Fence()
print 'rank %d has %s after put' % (rank, mem)
# Get and Accumulate
if rank == 0:
a = np.array(0.5, dtype='d')
# initialize acc with 0.0
acc = np.array(0.0, dtype='d')
# create window objects
win_a = MPI.Win.Create(a, comm=comm)
win_acc = MPI.Win.Create(acc, comm=comm)
# synchronize for win_a
win_a.Fence()
# synchronize for win_a
win_a.Fence()
# synchronize for win_acc
win_acc.Fence()
# synchronize for win_acc
win_acc.Fence()
# after accumulate, print the value of acc = 0.5 + 0.5 + 0.5
print 'rank 0 has acc = %s' % acc
else:
# initialize a with 0.0
a = np.array(0.0, dtype='d')
win_a = MPI.Win.Create(None, comm=comm)
win_acc = MPI.Win.Create(None, comm=comm)
# synchronize for win_a
win_a.Fence()
# get data from a memory window of rank 0
win_a.Get(a, target_rank=0)
# synchronize for win_a
win_a.Fence()
print 'rank %d has a = %s' % (rank, a)
# synchronize for win_acc
win_acc.Fence()
# each rank except 0 accumulates a to the memory window of rank 0
win_acc.Accumulate(a, target_rank=0, op=MPI.SUM)
# synchronize for win_acc
win_acc.Fence()
# free the window object
win.Free()
win_a.Free()
win_acc.Free()
運行結(jié)果如下:
$ mpiexec -n 4 python win.py
rank 0 puts [1 2] to rank 1
rank 3 has [0 0] after put
rank 2 has [0 0] after put
rank 1 has [1 2] after put
rank 1 has a = 0.5
rank 3 has a = 0.5
rank 2 has a = 0.5
rank 0 has acc = 1.5
以上我們介紹了 mpi4py 中的單邊通信相關(guān)操作,在下一篇中我們將介紹單邊通信的同步操作花枫。