2016-04-30 8 views
1

おはようゴーストゾーンを作成する適切な方法MPI [halos]

私は並列プログラミングコースに参加しています。教師は、ステンシル計算のためのドメインパーティションを含む割り当てを私たちに与えました。このタイプの計算(有限差分)の場合、コードを並列化する最も一般的な方法は、ドメインを分割していくつかのゴーストゾーン(ハロー)を作成することです。

私はこの簡単な例をプログラムして、内側の値= 123と境界値88を持ついくつかの配列を初期化しました。すべての通信の最後に、すべてのゴースト値は8のままでなければなりません。私は123の値を得ています。

(なし幽霊)シリアル:

123 - 123 - ... - 123 - 123 

2つのパーティション:

123 - 123 - ... - 88 ||| 88 - ... - 123 - 123 

3つのパーティション:

123 - 123 - ... - 88 ||| 88 - ... - 123 - 123 - 88 ||| 88 - ... - 123 - 123 

さておき、このバグから、ここでの主な問題は、正しいについてですゴーストゾーンを作成し維持するためのアプローチ。私の厄介な場合(MYID == ....それ以外の場合(MYID = ...人々は通常、並列処理のこの種を実装する方法を実装?の他のタイプ?

#include<mpi.h> 
#include<stdio.h> 
#include<stdlib.h> 

int WhichSize(int mpiId, int numProc, int tam); 

int main(int argc, char *argv[]){ 

    int i; 
    int localSize; 
    int numProc; 
    int myid; 

    int leftProc; 
    int rightProc; 

    int * myArray; 
    int fullDomainSize = 16; 

    MPI_Request request; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &numProc); 
    MPI_Comm_rank(MPI_COMM_WORLD, &myid); 


    // Lets get each partition size. 
    localSize = WhichSize(myid, numProc, fullDomainSize); 


    // Allocate arrays acording to proc number. 
    if(numProc == 1){ 

     //printf("Allocating Array for serial usage\n"); 
     myArray = (int*)malloc(localSize*sizeof(int)); 

    } else if(numProc == 2) { 

     //printf("Allocating Array for 2 proc usage\n"); 
     myArray = (int*)malloc((localSize+ 1)*sizeof(int)); 

    } else if(numProc > 2) { 

     if (myid == 0 || myid == numProc - 1){ 

      //printf("Allocating array for boundary nodes usage\n"); 
      myArray = (int*)malloc((localSize+ 1)*sizeof(int)); 

     } else { 

      //printf("Allocating array for inner nodes usage\n"); 
      myArray = (int*)malloc((localSize+ 2)*sizeof(int)); 

     } 

    } 


    // Now we will fill the arrays with a dummy value 123. For the 
    // boundaries (ghosts) we will fill than with 80. Just to differe 
    // ntiate. 

    if(numProc == 1){ 

     //printf("----------------------------------------\n"); 
     //printf("Filling the serial array with values... \n"); 

     for (i = 0; i<localSize; i++){ 
      myArray[i] = 123; 
     } 

    } else if(numProc == 2) { 

     ////printf("------------------------------------------------\n"); 
     //printf("Filling array for two proc usage with values... \n"); 

     for (i = 0; i<localSize; i++){ 
      myArray[i] = 123; 
     } 

     // ghost. 
     myArray[localSize+1] = 8; 

    } else if(numProc > 2) { 

     if (myid == 0 || myid == numProc - 1){ 

      //printf("--------------------------------------------------\n"); 
      //printf("Filling boundary node arrays usage with values... \n"); 

      for (i = 0; i<localSize; i++){ 
       myArray[i] = 123; 
      } 

      // ghosts. 
      myArray[localSize+1] = 8; 

     } else { 

      //printf("--------------------------------------------------\n"); 
      //printf("Filling inner node arrays usage with values... \n"); 

      for (i = 0; i<localSize; i++){ 
       myArray[i] = 123; 
      } 

      // ghosts. 
      myArray[localSize+1] = 8; 
      myArray[0] = 8; 

     } 

    } 


    // Now lets comunicate the ghosts with MPI_Sendrecv(). 

    if(numProc == 1){ 

     //printf("Serial usage, no ghost to comunicate \n"); 

    } else if(numProc == 2) { 

     if (myid == 0){ 

      //printf("Sending ghost value from proc %d to %d\n", myid, myid + 1); 
      MPI_Isend(&myArray[localSize+1], 
         1, 
         MPI_INT, 
         1, 
         12345, 
         MPI_COMM_WORLD, 
         &request); 

     } else if (myid == 1) { 

      //printf("Receiving ghost value from proc %d to %d\n", myid-1, myid); 
      MPI_Irecv(&myArray[localSize+1], 
         1, 
         MPI_INT, 
         0, 
         12345, 
         MPI_COMM_WORLD, 
         &request); 
     } 


    } else if(numProc > 2) { 

     if (myid == 0){ 

      rightProc = myid + 1; 

      if (myid == 0){ 

       //printf("-------------------------------\n"); 
       //printf("Communicating Boundary ghosts !\n"); 
       //printf("-------------------------------\n"); 

       //printf("Sending ghost value from proc %d to %d\n", myid, myid + 1); 
       MPI_Isend(&myArray[localSize+1], 
          1, 
          MPI_INT, 
          rightProc, 
          12345, 
          MPI_COMM_WORLD, 
          &request); 

      } else if (myid == rightProc) { 

       //printf("Receiving ghost value from proc %d to %d\n", myid-1, myid); 
       MPI_Irecv(&myArray[localSize+1], 
          1, 
          MPI_INT, 
          0, 
          12345, 
          MPI_COMM_WORLD, 
          &request); 
      } 

     } else if (myid == numProc - 1) { 

      leftProc = myid - 1; 

      if (myid == numProc - 1){ 

       //printf("-------------------------------\n"); 
       //printf("Communicating Boundary ghosts !\n"); 
       //printf("-------------------------------\n"); 

       ////printf("Sending ghost value from proc %d to %d\n", myid, myid + 1); 
       MPI_Isend(&myArray[localSize+1], 
          1, 
          MPI_INT, 
          leftProc, 
          12345, 
          MPI_COMM_WORLD, 
          &request); 

      } else if (myid == leftProc) { 

       rightProc = myid + 1; 

       //printf("Receiving ghost value from proc %d to %d\n", myid-1, myid); 
       MPI_Irecv(&myArray[localSize+1], 
          1, 
          MPI_INT, 
          rightProc, 
          12345, 
          MPI_COMM_WORLD, 
          &request); 
      } 

     } else { 

       //printf("-------------------------------\n"); 
       //printf("Communicating Inner ghosts baby\n"); 
       //printf("-------------------------------\n"); 

       leftProc = myid - 1; 
       rightProc = myid + 1; 

       // Communicate tail ghost. 
       if (myid == leftProc) { 
        MPI_Isend(&myArray[localSize+1], 
           1, 
           MPI_INT, 
           rightProc, 
           12345, 
           MPI_COMM_WORLD, 
           &request); 

       } else if (myid == rightProc){ 
        MPI_Irecv(&myArray[localSize+1], 
           1, 
           MPI_INT, 
           leftProc, 
           12345, 
           MPI_COMM_WORLD, 
           &request); 
       } 

       // Communicate head ghost. 
       if (myid == leftProc) { 
        MPI_Isend(&myArray[0], 
           1, 
           MPI_INT, 
           rightProc, 
           12345, 
           MPI_COMM_WORLD, 
           &request); 

       } else if (myid == rightProc){ 
        MPI_Irecv(&myArray[0], 
           1, 
           MPI_INT, 
           leftProc, 
           12345, 
           MPI_COMM_WORLD, 
           &request); 
       } 
     } 
    } 


    // Now I Want to see if the ghosts are in place !. 

    if (myid == 0){ 
     printf("The ghost value is: %d\n", myArray[localSize + 1]); 
    } else if (myid == numProc - 1){ 
     printf("The ghost value is: %d\n", myArray[0]); 
    } else { 
     printf("The head ghost is: %d\n", myArray[0]); 
     printf("The tail ghost is: %d\n", myArray[localSize + 1]); 
    } 


    MPI_Finalize(); 

    exit(0); 
} 

int WhichSize(int mpiId, int numProc, int tam){ 

    double resto; 
    int tamLocal; 

    tamLocal = tam/numProc; 

    resto = tam - tamLocal*numProc; 

    if (mpiId < resto) tamLocal = tamLocal + 1; 


    return tamLocal; 
} 

感謝別に、このためのクリーナー解決策はあります皆さん!

答えて

5

ハローは、エレガントなデカルト仮想トポロジーと送受信の操作を使用してMPIで実装することができる。すべての

まず、条件演算子では、ランク依存のロジックの多くを持つことが読みコードが硬くなり、理解してください。コードが対称であるとき、つまりすべてのランクが同じコードを実行するときには良い方法です。コーナーのケースは、 MPI_PROC_NULLヌルランクを使用する - そのランクとの間で送受信すると、ノーオペレーションが発生します。したがって、十分に行うことです。

// Compute the rank of the left neighbour 
leftProc = myid - 1; 
if (leftProc < 0) leftProc = MPI_PROC_NULL; 
// Compute the rank of the right neighbour 
rightProc = myid + 1; 
if (rightProc >= numProc) rightProc = MPI_PROC_NULL; 

// Halo exchange in forward direction 
MPI_Sendrecv(&myArray[localSize], 1, MPI_INT, rightProc, 0, // send last element to the right 
      &myArray[0], 1, MPI_INT, leftProc, 0,   // receive into left halo 
      MPI_COMM_WORLD); 
// Halo exchange in reverse direction 
MPI_Sendrecv(&myArray[1], 1, MPI_INT, leftProc, 0,   // send first element to the left 
      &myArray[localSize+1], 1, MPI_INT, rightProc, 0, // receive into right halo 
      MPI_COMM_WORLD); 

コードも両端にそれらのため、任意のランクのために動作すること - が送信元または送信先のいずれかがnullのランクであり、実際の転送は、対応する方向に発生していません。また、1つから複数のMPIプロセスでも動作します。すべてのランクには、それが本当に必要でないもの(2つのコーナーランク)を含め、両サイドにハローが必要です。それらのダミーハローには、境界値などの有益なもの(PDEを解くときなど)を保存することができます。

コードでは、ノンブロッキング操作を間違って使用しています。それらは扱いにくく、注意が必要です。代わりにMPI_Sendrecvを使用できます。送信と受信の両方の操作を同時に実行し、デッドロックを防止します(各送信に一致する受信がある限り)。

ドメインが周期的である場合には、ランク計算論理は単純に次のようになります。

// Compute the rank of the left neighbour 
leftProc = (myid - 1 + numProc) % numProc; 
// Compute the rank of the right neighbour 
rightProc = (myid + 1) % numProc; 

代わりの算術演算を行って、1は、デカルトの仮想トポロジを作成し、2人の隣人のランクを見つけるためにMPI_Cart_shiftを使用することができます:

// Create a non-periodic 1-D Cartesian topology 
int dims[1] = { numProc }; 
int periods[1] = { 0 }; // 0 - non-periodic, 1 - periodic 
MPI_Comm cart_comm; 
MPI_Cart_create(MPI_COMM_WORLD, 1, dims, periods, 1, &cart_comm); 

// Find the two neighbours 
MPI_Cart_shift(cart_comm, 0, 1, &leftProc, &rightProc); 

ハロー交換のためのコードはcart_commMPI_COMM_WORLDを交換する必要があることを唯一の違いと同じまま。 MPI_Cart_shiftが自動的にコーナーケースを処理し、適切な場合はMPI_PROC_NULLを返します。この方法の利点は、periods[]アレイ内の値を反転するだけで、非周期領域と周期領域を簡単に切り替えることができることです。

ハローは、アルゴリズムに応じて必要に応じて頻繁に更新する必要があります。ほとんどの反復スキームでは、各反復の開始時に更新が行われなければなりません。マルチレベルのハローを導入し、外側のレベルの値を使用して内側のレベルの値を計算することによって、通信頻度を減らすことができます。

締結することは、あなたのmain関数が(デカルトトポロジを使用せず)に減らすことができます。

int main(int argc, char *argv[]){ 

    int i; 
    int localSize; 
    int numProc; 
    int myid; 

    int leftProc; 
    int rightProc; 

    int * myArray; 
    int fullDomainSize = 16; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &numProc); 
    MPI_Comm_rank(MPI_COMM_WORLD, &myid); 

    // Compute neighbouring ranks 
    rightProc = myid + 1; 
    if (rightProc >= numProc) rightProc = MPI_PROC_NULL; 
    leftProc = myid - 1; 
    if (leftProc < 0) leftProc = MPI_PROC_NULL; 

    // Lets get each partition size. 
    localSize = WhichSize(myid, numProc, fullDomainSize); 

    // Allocate arrays. 
    myArray = (int*)malloc((localSize+ 2)*sizeof(int)); 

    // Now we will fill the arrays with a dummy value 123. For the 
    // boundaries (ghosts) we will fill than with 80. Just to differe 
    // ntiate. 

    //printf("--------------------------------------------------\n"); 
    //printf("Filling node arrays usage with values... \n"); 

    for (i = 1; i<localSize; i++){ 
     myArray[i] = 123; 
    } 

    // ghosts. 
    myArray[localSize+1] = 8; 
    myArray[0] = 8; 

    //printf("-------------------------------\n"); 
    //printf("Communicating Boundary ghosts !\n"); 
    //printf("-------------------------------\n"); 

    //printf("Sending ghost value to the right\n"); 
    MPI_Sendrecv(&myArray[localSize], 1, MPI_INT, rightProc, 12345, 
       &myArray[0], 1, MPI_INT, leftProc, 12345, 
       MPI_COMM_WORLD); 

    //printf("Sending ghost value to the left\n"); 
    MPI_Sendrecv(&myArray[1], 1, MPI_INT, leftProc, 12345, 
       &myArray[localSize+1], 1, MPI_INT, rightProc, 12345, 
       MPI_COMM_WORLD); 

    // Now I Want to see if the ghosts are in place !. 

    printf("[%d] The head ghost is: %d\n", myid, myArray[0]); 
    printf("[%d] The tail ghost is: %d\n", myid, myArray[localSize + 1]); 

    MPI_Finalize(); 

    return 0; 
} 
+0

は、ご返信いただきありがとうございます! –

関連する問題