轉自:https://blog.csdn.net/sinat_22336563/article/details/70229243
參考:http://mpitutorial.com/tutorials/performing-parallel-rank-with-mpi/
一、 MPI_Scatter
MPI_Scatter與MPI_Bcast非常相似,都是一對多的通信方式,不同的是后者的0號進程將相同的信息發送給所有的進程,而前者則是將一段array 的不同部分發送給所有的進程,其區別可以用下圖概括:
0號進程分發數據的時候是根據進程的編號進行的,array中的第一個元素發送給0號進程,第二個元素則發送給1號進程,以此類推。
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)
通常send_count等於array的元素個數除以進程個數。
二、 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)
四、實例
問題描述:
我們的函數需要在每個進程中取一個數字,並返回其所有流程中所有其他數字的相關排名。 與此同時,我們將需要其他雜項信息,例如正在使用的通信器以及正在排名的數字的數據類型。
整體函數表示:
TMPI_Rank( void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm)
TMPI_Rank接收一個包含一個datatype類型的send_data緩沖區。 recv_data在包含send_data的rank值的每個進程上只收到一個整數。 comm變量是正在進行排名的通信器。
解決並行排序問題的第一步是排序所有進程的所有數字。 這必須完成,以便我們可以在整個數字集中找到每個數字的排名。 有很多方法可以做到這一點。 最簡單的方法是將所有數字收集到一個進程並對數字進行排序。
void *gather_numbers_to_root(void *number, MPI_Datatype datatype, MPI_Comm comm) { int comm_rank, comm_size; MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); // 根據使用的數據類型,給根進程分配size int datatype_size; MPI_Type_size(datatype, &datatype_size); void *gathered_numbers; if (comm_rank == 0) { gathered_numbers = malloc(datatype_size * comm_size); } // 收集根進程的所有數字 MPI_Gather(number, 1, datatype, gathered_numbers, 1, datatype, 0, comm); return gathered_numbers; }
根進程必須在此函數中收集comm_size數字,所以它malloc一個datatype_size * comm_size長度的數組。在使用MPI_Gather在根進程上收集數字之后,數字必須在根進程中進行排序,以便可以確定其編號。
先定義一個結構體
typedef struct { int comm_rank; union { float f; int i; } number; } CommRankNumber;
排序使用C標准庫函數:
int *get_ranks(void *gathered_numbers, int gathered_number_count, MPI_Datatype datatype) { int datatype_size; MPI_Type_size(datatype, &datatype_size); //將收集的數字數組轉換為CommRankNumbers數組。 // 這使我們能夠對數字進行排序,並保留擁有數字的進程信息。 CommRankNumber *comm_rank_numbers = malloc( gathered_number_count * sizeof(CommRankNumber)); int i; for (i = 0; i < gathered_number_count; i++) { comm_rank_numbers[i].comm_rank = i; memcpy(&(comm_rank_numbers[i].number), gathered_numbers + (i * datatype_size), datatype_size); } // 根據數據類型進行排序 if (datatype == MPI_FLOAT) { qsort(comm_rank_numbers, gathered_number_count, sizeof(CommRankNumber), &compare_float_comm_rank_number); } else { qsort(comm_rank_numbers, gathered_number_count, sizeof(CommRankNumber), &compare_int_comm_rank_number); } // comm_rank_numbers被排序,為每個進程創建一個編號數組。 該數組的第i個元素包含進程i發送的數字的編號。數字排序后,我們必須以正確的順序創建一個排列數組,以便它們可以scatter回請求進程。 int *ranks = (int *)malloc(sizeof(int) * gathered_number_count); for (i = 0; i < gathered_number_count; i++) { ranks[comm_rank_numbers[i].comm_rank] = i; } // Clean up and return the rank array free(comm_rank_numbers); return ranks; }
綜合可得:
int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype, MPI_Comm comm) { // 首先檢查基本情況 - 僅支持此函數的MPI_INT和MPI_FLOAT。 if (datatype != MPI_INT && datatype != MPI_FLOAT) { return MPI_ERR_TYPE; } int comm_size, comm_rank; MPI_Comm_size(comm, &comm_size); MPI_Comm_rank(comm, &comm_rank); // 要計算編號,我們必須將數字收集到一個進程中,對數字進行排序,然后分散結果的等級值。 //首先收集comm的進程0的數字。 void *gathered_numbers = gather_numbers_to_root(send_data, datatype, comm); // 獲得每個進程的編號 int *ranks = NULL; if (comm_rank == 0) { ranks = get_ranks(gathered_numbers, comm_size, datatype); } // Scatter the rank results MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm); // Do clean up if (comm_rank == 0) { free(gathered_numbers); free(ranks); } }
流程如下:
五、總結
本節介紹了三種聚合通信,分別對應一對多,多對一,多對多通信。