在上一篇中我們介紹了 mpi4py 中的單邊通信相關(guān)操作再菊,下面我們將介紹單邊通信的同步操作。
單邊通信(遠(yuǎn)端內(nèi)存訪問)操作包括以下兩個(gè)范疇:
- 主動(dòng)目標(biāo)通信颜曾。與點(diǎn)到點(diǎn)通信類似纠拔,數(shù)據(jù)從一個(gè)進(jìn)程的內(nèi)存?zhèn)魉偷搅硪粋€(gè)進(jìn)程的內(nèi)存中,兩個(gè)進(jìn)程都直接參與通信泛豪。
- 被動(dòng)目標(biāo)通信稠诲。數(shù)據(jù)仍從一個(gè)進(jìn)程轉(zhuǎn)移到另外一個(gè)進(jìn)程,但這個(gè)進(jìn)程是一個(gè)第三方進(jìn)程候址。真正參與通信的兩個(gè)進(jìn)程都是源——一個(gè)負(fù)責(zé)將數(shù)據(jù) put 到第三方進(jìn)程吕粹,另一個(gè)則從第三方進(jìn)程 get 到本地內(nèi)存中种柑。
與此對應(yīng)岗仑,MPI 提供以下三種遠(yuǎn)端內(nèi)存訪問的同步機(jī)制:
-
Fence:主動(dòng)同步
- 提供并行計(jì)算中常用到的寬松同步模型,主要用于活動(dòng)目標(biāo)通信聚请;
- 在同一個(gè) window 對象組中的所有進(jìn)程都需要調(diào)用 Fence 來啟動(dòng)一個(gè)數(shù)據(jù)交換階段荠雕;
- 數(shù)據(jù)交換階段中所有進(jìn)程都可以發(fā)出讀寫操作請求;
- 在同一個(gè) window 對象組中的所有進(jìn)程都需要調(diào)用 Fence 來結(jié)束一個(gè)數(shù)據(jù)交換階段驶赏;
- 第二次 Fence 同步完成時(shí)所有的操作請求都已完成炸卑。
-
Start/Post,Wait/Complete 同步:主動(dòng)同步
- 也用于活動(dòng)目標(biāo)通信煤傍,適用于需最大限度減少同步操作的場合——僅對一組進(jìn)程進(jìn)行同步盖文;
- 和 Fence 類似,但源進(jìn)程和目標(biāo)進(jìn)程指出他們可以和哪一個(gè)組的進(jìn)程進(jìn)行通信蚯姆;
- 目標(biāo)進(jìn)程:啟動(dòng)一個(gè)暴露階段(Exposure Epoch): Post 打開五续,Wait 關(guān)閉洒敏;
- 源進(jìn)程:啟動(dòng)一個(gè)訪問階段(Access epoch):Start 打開,Complete 關(guān)閉疙驾。
-
Lock/Unlock:被動(dòng)同步
- 單邊非同步通信凶伙;
- 目標(biāo)進(jìn)程不主動(dòng)參與通信過程;
- Lock/Unlock開始/結(jié)束被動(dòng)通信階段它碎;
- 可通過此類同步操作模擬共享內(nèi)存訪問模式——即公告牌模式函荣,進(jìn)程可以隨機(jī)方式訪問/更新公告牌的不同部分;
- Flush:完成所有還沒完成的操作扳肛。
同步方法
下面給出同步操作相關(guān)的方法(MPI.Win 類方法)接口:
Fence
Fence(self, int assertion=0)
對遠(yuǎn)端內(nèi)存訪問操作進(jìn)行同步傻挂,該操作對 windows 對象所屬進(jìn)程組內(nèi)的所有進(jìn)程執(zhí)行集合操作。assertion
參數(shù)主要用于在具體的調(diào)用上下文中設(shè)置優(yōu)化措施挖息,默認(rèn)值為 0踊谋,其它有效的值有:
- MPI.MODE_NOSTORE,表示自上一個(gè)同步操作以來旋讹,本地窗口并沒有被本地的 store 操作殖蚕、本地發(fā)起的 get 操作、本地進(jìn)程執(zhí)行的 receive 操作所修改過沉迹。
- MPI.MODE_NOPUT睦疫,在此次 Fence 執(zhí)行之后,直到配對的同步 Fence 執(zhí)行之前鞭呕,本地窗口都不會(huì)被 put 或 accumulate 操作更新蛤育。
- MPI.MODE_NOPRECEDE,本次 Fence 不會(huì)在本地啟動(dòng)任何遠(yuǎn)端內(nèi)存操作葫松。當(dāng)前組內(nèi)所有進(jìn)程都必須同時(shí)指定該選項(xiàng)瓦糕。
- MPI.MODE_NOSUCCEED,本次 Fence 不會(huì)在本地啟動(dòng)任何遠(yuǎn)端內(nèi)存操作腋么。當(dāng)前組內(nèi)所有進(jìn)程都必須同時(shí)指定該選項(xiàng)咕娄。
Start/Complete,Post/Wait珊擂,Test
Start(self, Group group, int assertion=0)
在源進(jìn)程啟動(dòng)訪問時(shí)間段和開放時(shí)間段圣勒。group
參數(shù)是源進(jìn)程同步操作涉及的進(jìn)程組,assertion
參數(shù)設(shè)置源進(jìn)程同步操作的優(yōu)化條件摧扇。在時(shí)間段內(nèi)所有內(nèi)存訪問操作僅允許訪問 group
參數(shù)中指定的進(jìn)程的窗口圣贸,組內(nèi)被訪問的進(jìn)程都要通過 Post 啟動(dòng)配對的同步操作。源進(jìn)程啟動(dòng)內(nèi)存訪問之后扛稽,直到目標(biāo)進(jìn)程啟動(dòng)配對的 Post 之后才可實(shí)際影響目標(biāo)進(jìn)程的窗口吁峻。
Complete(self)
與 Start 配對,結(jié)束源進(jìn)程的訪問時(shí)間段。會(huì)在源進(jìn)程一側(cè)強(qiáng)制完成訪問時(shí)間段內(nèi)所有的遠(yuǎn)端內(nèi)存操作用含,但不會(huì)影響目標(biāo)進(jìn)程橙困。
Post(self, Group group, int assertion=0)
在目標(biāo)進(jìn)程啟動(dòng)訪問時(shí)間段和開放時(shí)間段。group
參數(shù)是源進(jìn)程同步操作涉及的進(jìn)程組耕餐,assertion
參數(shù)設(shè)置源進(jìn)程同步操作的優(yōu)化條件凡傅。
Wait(self)
與 Post 配對,二者分別定義一個(gè)開放時(shí)間段的起始和結(jié)束肠缔。Wait 會(huì)阻塞夏跷,并與允許在此時(shí)間段內(nèi)訪問窗口對象的所有源進(jìn)程的 Complete 相匹配,以此來確保所有源進(jìn)程在窗口對象的本地副本上完成遠(yuǎn)端內(nèi)存訪問明未,而當(dāng) Wait 返回時(shí)槽华,意味著目標(biāo)進(jìn)程窗口對象上的遠(yuǎn)端內(nèi)存訪問操作已完成。
Test(self)
Wait 的非阻塞版本趟妥,如果返回 True猫态,則相當(dāng)于調(diào)用 Wait 返回,如果返回 False披摄,則沒有什么可見的效應(yīng)亲雪。
Lock/Unlock
Lock(self, int rank, int lock_type=LOCK_EXCLUSIVE, int assertion=0)
被動(dòng)目標(biāo)同步操作,標(biāo)記一個(gè)訪問時(shí)間段的開始疚膊,此期間僅允許由參數(shù) rank
指定進(jìn)程的窗口對象可被遠(yuǎn)端內(nèi)存訪問义辕。locak_type
可以是默認(rèn)的 MPI.LOCK_EXCLUSIVE(互斥鎖)或 MPI.LOCK_SHARED(共享鎖)。assertion
除了默認(rèn)的 0 之外可以設(shè)置為 MPI.MODE_NOCHECK寓盗,表示在嘗試創(chuàng)建鎖時(shí)灌砖,可以確信沒有其它進(jìn)程已經(jīng)取得了相同窗口對象的鎖,或者正在嘗試獲取窗口對象的鎖傀蚌。
Unlock(self, int rank)
與 Lock 配對基显,標(biāo)記一個(gè)訪問時(shí)間段的結(jié)束。
例程
下面給出單邊通信操作的同步相關(guān)方法的使用例程善炫。
# win_sync.py
"""
Demonstrates the usage of Start, Complete, Post, Wait, Lock, Unlock.
Run this with 2 processes like:
$ mpiexec -n 2 python win.py
"""
import numpy as np
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
SIZE1 = 5
SIZE2 = 10
if rank == 0:
A = np.zeros(SIZE2, dtype='i') # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
win = MPI.Win.Create(None, comm=comm)
# create a group with rank 1 only
grp = comm.group.Incl(ranks=[1])
# start remote memory access
win.Start(grp)
# put the first 5 elements of A of rank 0 to A[:5] of rank 1
win.Put(A[:SIZE1], target_rank=1)
# end remote memory access
win.Complete()
# lock to protect the get operation
win.Lock(rank=1, lock_type=MPI.LOCK_SHARED)
# get last 5 elements of A of rank 1 to A[:5] of rank 0
win.Get(A[:SIZE1], target_rank=1, target=[5*4, 5, MPI.INT])
# unlock after the get operation
win.Unlock(rank=1)
print 'rank 0 has A = %s' % A
else:
A = np.zeros(SIZE2, dtype='i') + 1 # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
win = MPI.Win.Create(A, comm=comm)
# create a group with rank 0 only
grp = comm.group.Incl(ranks=[0])
# start remote memory access
win.Post(grp)
# end remote memory access
win.Wait()
# no need for Lock and Unlock here
print 'rank 1 has A = %s' % A
運(yùn)行結(jié)果如下:
$ mpiexec -n 2 python win_sync.py
rank 1 has A = [0 0 0 0 0 1 1 1 1 1]
rank 0 has A = [1 1 1 1 1 0 0 0 0 0]
以上我們介紹了 mpi4py 中的單邊通信的同步操作撩幽,在下一篇中我們將介紹并行 I/O。