MPI知識點總結
- 點對點
- 阻塞式通信
- MPI_Send
- MPI_Recv
- 非阻塞式通信
- MPI_ISend
- MPI_IRecv
- MPI_Wait
- 阻塞式通信
- 同步聚合
- 柵欄同步
- MPI_Barrier
- 柵欄同步
- 通信聚合
- 廣播
- MPI_Bcast
- 分散
- MPI_Scatter
- 收集
- MPI_Gather
- 全部收集
- MPI_Allgather
- 廣播
- 規約聚合
- 規約操作
- MPI_Reduce
- MPI_Allreduce
- 規約操作
幾個重要函數
MPI_Scatter
MPI_Scatter與MPI_Bcast非常相似,都是一對多的通信方式,不同的是後者的0號進程將相同的信息發送給所有的進程,而前者則是將一段array 的不同部分發送給所有的進程,其區別可以用下圖概括:
MPI_Scatter(
void* send_data,//存儲在0號進程的數據,array
int send_count,//具體需要給每個進程發送的數據的個數
//如果send_count爲1,那麼每個進程接收1個數據;如果爲2,那麼每個進程接收2個數據
MPI_Datatype send_datatype,//發送數據的類型
void* recv_data,//接收緩存,緩存 recv_count個數據
int recv_count,
MPI_Datatype recv_datatype,
int root,//root進程的編號
MPI_Comm communicator)
MPI_Gather
MPI_Gather和MPI_scatter剛好相反,他的作用是從所有的進程中將每個進程的數據集中到根進程中,同樣根據進程的編號對array元素排序,如圖所示:
MPI_Gather(
void* send_data,
int send_count,
MPI_Datatype send_datatype,
void* recv_data,
int recv_count,//注意該參數表示的是從單個進程接收的數據個數,不是總數
MPI_Datatype recv_datatype,
int root,
MPI_Comm communicator)
MPI_Allgather
當數據分佈在所有的進程中時,MPI_Allgather將所有的數據聚合到每個進程中。
MPI_Allgather(
void* send_data,
int send_count,
MPI_Datatype send_datatype,
void* recv_data,
int recv_count,
MPI_Datatype recv_datatype,
MPI_Comm communicator)
MPI_Allgatherv
/*
* 收集所有任務的數據並將組合數據傳送到所有任務
* int MPI_Allgatherv(const void * sendbuf,int sendcount,MPI_Datatype sendtype,
* void * recvbuf,const int * recvcounts,const int * displs,
* MPI_Datatype recvtype,MPI_Comm comm)
*
* sendbuf:要發送內容的起始地址
* sendcount:要發送的數量
* sendtype:要發送數據的類型
* recvbuf:接收數據要存放的單元的地址
* recvcounts:這是一個整數數組,包含從每個進程要接收的數據量,比如{0, 1} 從0號進程接收0個,從1號進程接收1個
* displs:這是一個整數數組,包含存放從每個進程接收的數據相對於recvbuf的偏移地址
* recvtype:要接收的數據類型
* comm:通信集合
*/
MPI_Allagtherv解決MPI_Allagther要求每個進程處理任務量必須均等的問題,通過recvcounts數組指定每個進程接收任務的數量,通過displs數組指定每個進程發送的數據在接收緩衝區中的偏移量。
MPI_Alltoall
int MPI_Alltoall(const void *sendbuf, //發送緩衝區的起始地址
int sendcount, //要發送的數量
MPI_Datatype sendtype, //發送的數據類型
void *recvbuf, //接收緩衝區的起始位置
int recvcount, //要接收的數量
MPI_Datatype recvtype, //要接收的類型
MPI_Comm comm) //通信子
函數原理示意圖如下,分別演示了發送緩衝區數據量爲1和2時的發送和接收情況:
MPI_Alltoallv
有時候,我們當前進程往其他進程發送的數據不一樣,個數也不一樣,這個時候就需要用MPI_Alltoallv來解決。該函數用法類似於MPI_Allgather和MPI_Allgatherv的區別,即發送緩衝區的大小可以不相同(通過數組指定大小)。
int MPI_Alltoallv(const void *sendbuf,
const int *sendcounts,
const int *sdispls,
MPI_Datatype sendtype,
void *recvbuf,
const int *recvcounts,
const int *rdispls,
MPI_Datatype recvtype,
MPI_Comm comm)
相比MPI_Alltoall,這個方法有幾個參數不一樣:
int* sendcounts 和int *recvcounts。這兩個參數你可以把它當做兩個數組,數組中的元素代表往其他節點各發送(接收)多少數據。比如說,sendcounts[0]=3,sendcounts[1]=4,代表該節點要往0號節點發送3個sendtype的數據,往1號節點發送4個sendtype的數據。
多了兩個參數,int *sdispls和int *rdispls,這兩個可以看做是數組,數組中的每個元素代表了要發送(接收)的那塊數據相對於緩衝區起始位置的位移量。
MPI_Reduce
歸約聚合類似於收集,但對收集的數據執行某種歸約操作,例如計算總和,查找最大值或執行某些用戶定義的操作。
規約函數 MPI_Reduce(),將通信子內各進程的同一個變量參與規約計算,並向指定的進程輸出計算結果。
MPI_METHOD MPI_Reduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf, // 指向輸入數據的指針
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向輸出數據的指針,即計算結果存放的地方
_In_range_(>= , 0) int count, // 數據尺寸,可以進行多個標量或多個向量的規約
_In_ MPI_Datatype datatype, // 數據類型
_In_ MPI_Op op, // 規約操作類型
_mpi_coll_rank_(root) int root, // 目標進程號,存放計算結果的進程
_In_ MPI_Comm comm // 通信子
);
demo:
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
data = rank;// 參與計算的數據
MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);// 所有的進程都要調用,而不是隻在目標進程中調用
MPI_Finalize();
}
MPI_Allreduce
規約並廣播函數 MPI_Allreduce(),在計算規約的基礎上,將計算結果分發到每一個進程中,相比於 MPI_Reduce(),只是少了一個 root 參數。除了簡單的先規約再廣播的方法,書中介紹了蝶形結構全局求和的方法。
少了一個root參數是因爲該函數將計算的結果廣播到每個進程中去,各個進程均更新數據。
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allreduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf,
_Out_opt_ void* recvbuf,
_In_range_(>= , 0) int count,
_In_ MPI_Datatype datatype,
_In_ MPI_Op op,
_In_ MPI_Comm comm
);
demo:
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
data = rank;
MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);// 所有的進程都要調用
MPI_Finalize();
}