所以這有點微妙,需要對Gather集合如何放置複雜類型有所瞭解。
如果你看最多examples of MPI_Gather,它們是一維數組,它很容易解釋應該發生什麼;你會從每個進程中獲得(比如說)10個整數,而且Gather很聰明,可以從開始的0級開始放入10個整數,在陣列的10-19個開始放置10個整數,依此類推。
雖然這樣更復雜的佈局稍微複雜一點。首先,從發送者的角度來看,數據佈局與接收者的數據佈局不同。從發送者的角度來看,您從數組元素[1][2]
開始,轉到[1][5]
(數組大小爲7x7),然後跳轉到數組元素[2][3]
- [2][5]
等。 有CHUNK_ROWS個數據塊,每個數據塊之間用2個整數。
現在考慮接收者如何接收它們。假設它正在接收0級的數據。它將接收到數組元素[0][0]-[0][4]
- 迄今爲止非常好;但是接下來它將接收到大小爲10x10的數組[1][0]-[1][4]
中的下一個數據塊。這是跳過5個元素。內存中的佈局不同。因此,接收器將不得不接收不同的Subarray
類型,然後發送者從中發送,因爲存儲器佈局是不同的。
因此,儘管你可以從一些可以發送看起來像這樣:
sizes[0] = CHUNK_ROWS+2;
sizes[1] = CHUNK_COLUMNS+2;
subsizes[0] = CHUNK_ROWS;
subsizes[1] = CHUNK_COLUMNS;
starts[0] = 1;
starts[1] = 1;
MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&sendsubarray);
MPI_Type_commit(&sendsubarray);
您將會收到到的東西,看起來像這樣:
sizes[0] = ROWS; sizes[1] = COLUMNS;
subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS;
starts[0] = 0; starts[1] = 0;
MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&recvsubarray);
MPI_Type_commit(&recvsubarray);
最重要的是,注意到在區別sizes
數組。
現在我們正在接近一點。請注意您的MPI_Gather線變爲這樣:
MPI_Gather(DEBUG_CH[0],1,sendsubarray,recvptr,1,recvsubarray,0,MPI_COMM_WORLD);
有幾件事情沒有工作對以前的版本,MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,0,MPI_COMM_WORLD);
- 首先,請注意,如果引用ddd[0]
,但除了每個等級等級0,ddd=NULL
,所以這將失敗。因此創建一個名爲say recvptr
的新變量,並在等級零中將其設置爲ddd[0]
。 (其他進程認爲它並不重要,因爲他們沒有收到。)另外,我認爲你不想接收CHUNK_ROWS*CHUNK_COLUMS
MPI_INTs
,因爲這會將它們連續放置在內存中,我的理解是你希望它們像從屬任務一樣排列,但是在更大的陣列中。
好的,現在我們正在某處,但上述仍然將無法正常工作,一個有趣的原因。對於1d陣列示例,很容易計算出第n個排序數據的位置。計算的方式是找到正在收到的數據的範圍,並在該值後面開始下一個元素。但這在這裏不起作用。 「就在」等級0的數據末尾不是排名第一的數據應該開始的地方([0][5]
),而是[4][5]
- 排名0s子陣列中的最後一個元素之後的元素。在這裏,你從不同級別收集的數據重疊!所以我們將不得不擺弄數據類型的範圍,並手動指定每個等級數據的起始位置。第二個是容易的部分;當您需要手動指定每個處理器的數據量或其位置時,可以使用MPI_Gatherv函數。首先是棘手的部分。
MPI讓我們來指定給定數據類型的下限和上限 - 在給定一段內存的情況下,此類型的第一位數據將會出現,以及「結束」的位置,這只是意味着下一個可以開始的地方。 (數據可以延伸超過類型的上限,我認爲這些名稱使得這些名稱具有誤導性,但事實就是這樣。)您可以指定這是任何您喜歡的方式,以方便您;由於我們將處理數組int
中的元素,因此讓我們將我們的MPI_INT類型的大小設置爲大小。 (注意,我們只需要爲接收的類型做這件事;從發送類型來看,因爲我們只發送其中之一,所以沒關係)。
現在,我們將使用gatherv指定每個元素的起始位置 - 以這個新調整大小類型的「大小」爲單位,該大小僅爲1整數。因此,如果我們想要在[0][5]
處進入大陣列,大陣列的起始位移爲5;如果我們希望它在位置[5][5]
處移動,則位移爲55.
最後,請注意,收集和分散集體都假定即使「主」也參與。如果連主人都有他們自己的全局數組,那麼最簡單的方法就是讓它工作。
因此,既然,我以下工作:
#include <mpi.h>
#include <iostream>
#include <cstdlib>
using namespace std;
#define ROWS 10
#define COLUMNS 10
#define CHUNK_ROWS 5
#define CHUNK_COLUMNS 5
#define TAG 0
int** alloca_matrice(int righe, int colonne)
{
int** matrice=NULL;
int i;
matrice = (int **)malloc(righe * sizeof(int*));
if(matrice != NULL){
matrice[0] = (int *)malloc(righe*colonne*sizeof(int));
if(matrice[0]!=NULL)
for(i=1; i<righe; i++)
matrice[i] = matrice[0]+i*colonne;
else{
free(matrice);
matrice = NULL;
}
}
else{
matrice = NULL;
}
return matrice;
}
int main(int argc, char* argv[])
{
int my_id, numprocs,length,i,j;
int ndims, sizes[2],subsizes[2],starts[2];
int** DEBUG_CH=NULL;
int** ddd=NULL;
int *recvptr=NULL;
char name[BUFSIZ];
MPI_Datatype sendsubarray;
MPI_Datatype recvsubarray;
MPI_Datatype resizedrecvsubarray;
//MPI_Status status;
MPI_Init(&argc, &argv) ;
MPI_Comm_rank(MPI_COMM_WORLD, &my_id) ;
MPI_Comm_size(MPI_COMM_WORLD, &numprocs) ; // Ottiene quanti processi sono attivi
if (numprocs != 4) {
MPI_Abort(MPI_COMM_WORLD,1);
}
MPI_Get_processor_name(name, &length);
//creo una sottomatrice ripulita dalle ghost cells
ndims=2;
sizes[0] = CHUNK_ROWS+2;
sizes[1] = CHUNK_COLUMNS+2;
subsizes[0] = CHUNK_ROWS;
subsizes[1] = CHUNK_COLUMNS;
starts[0] = 1;
starts[1] = 1;
MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&sendsubarray);
MPI_Type_commit(&sendsubarray);
DEBUG_CH = alloca_matrice(CHUNK_ROWS+2,CHUNK_COLUMNS+2);
for(i=0; i<CHUNK_ROWS+2; i++){
for(j=0; j<CHUNK_COLUMNS+2; j++){
if(i==0 || i==CHUNK_ROWS+1 || j==0 || j==CHUNK_COLUMNS+1)
DEBUG_CH[i][j] = 5;
else
DEBUG_CH[i][j] = my_id;
}
}
recvptr=DEBUG_CH[0];
if(my_id==0){
ddd = alloca_matrice(ROWS,COLUMNS);
sizes[0] = ROWS; sizes[1] = COLUMNS;
subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS;
starts[0] = 0; starts[1] = 0;
MPI_Type_create_subarray(2,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&recvsubarray);
MPI_Type_commit(&recvsubarray);
MPI_Type_create_resized(recvsubarray, 0, 1*sizeof(int), &resizedrecvsubarray);
MPI_Type_commit(&resizedrecvsubarray);
recvptr = ddd[0];
}
int counts[5]={1,1,1,1};
int disps[5] ={0,5,50,55};
MPI_Gatherv(DEBUG_CH[0],1,sendsubarray,recvptr,counts,disps,resizedrecvsubarray,0,MPI_COMM_WORLD);
if(!my_id){
for(i=0; i<ROWS; i++){
for(j=0; j<COLUMNS; j++){
printf("%d ",ddd[i][j]);
}
printf("\n");
}
}
if(my_id == 0) {
MPI_Type_free(&resizedrecvsubarray);
MPI_Type_free(&recvsubarray);
free(ddd[0]);
free(ddd);
} else {
MPI_Type_free(&sendsubarray);
free(DEBUG_CH[0]);
free(DEBUG_CH);
}
MPI_Finalize(); // Chiusura di MPI.
return 0;
}
哇。我想我不明白mpi_gather是如何工作的......非常感謝。我將打印您的答案並將其作爲備忘錄。 – Riff 2011-04-07 19:48:20