2011-04-07 46 views
11

我必須解決一個小問題。我有4個從屬進程,每個進程都希望發送一個2d子數組(CHUNK_ROWS X CHUNK_COLUMNS)給主機0.主機0收集所有在ddd [ROWS] [COLUMNS]中的數據塊並打印出來。我想用MPI_Gather()MPI_Type_create_subarray和MPI_Gather

#include <mpi.h> 
#include <iostream> 
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; 
char name[BUFSIZ]; 
MPI_Datatype subarray=NULL; 
//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 
MPI_Get_processor_name(name, &length);  

if(my_id!=0){ 
    //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,&subarray); 
    MPI_Type_commit(&subarray); 

    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] = 1; 
    } 
    } 
//MPI_Send(DEBUG_CH[0],1,subarray,0,TAG,MPI_COMM_WORLD); 
} 
if(my_id==0){ 
ddd = alloca_matrice(ROWS,COLUMNS); 
} 

MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,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) 
MPI_Type_free(&subarray); 

MPI_Finalize();        // Chiusura di MPI. 
return 0; 
} 

謝謝大家。

回答

20

所以這有點微妙,需要對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_COLUMSMPI_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; 
} 
+0

哇。我想我不明白mpi_gather是如何工作的......非常感謝。我將打印您的答案並將其作爲備忘錄。 – Riff 2011-04-07 19:48:20