MPI type create subarray and MPI Gather

Muszę rozwiązać mały problem z mpi. Mam 4 procesy niewolników i każdy z nich chce wysłać 2D subarray (CHUNK_ROWS X CHUNK_COLUMNS) do master 0. Master 0 zbiera wszystkie kawałki w ddd [ROWS] [COLUMNS] i wypisuje je. Chcę użyć 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;
}
Dzięki wszystkim.
Author: Jonathan Dursi, 2011-04-07

1 answers

Jest to więc nieco bardziej subtelne i wymaga pewnego zrozumienia tego, w jaki sposób zbiorowi lokują złożone typy.

Jeśli spojrzysz na większość przykładów mpi_gather , są to tablice 1-d i dość łatwo zinterpretować, co powinno się wydarzyć; otrzymujesz (powiedzmy) 10 Int z każdego procesu, a Gather jest wystarczająco inteligentny, aby umieścić 10 Int z rank 0 Na początku, 10 z rank 1 na pozycjach 10-19 w tablicy, i tak dalej.

Bardziej złożone układy jak ten są trochę bardziej skomplikowane. Po pierwsze, układ danych z punktu widzenia nadawcy różni się od układu danych z odbiorników. Z punktu widzenia nadawcy, zaczynasz od elementu tablicy [1][2], przejdź do [1][5] (w tablicy o rozmiarze 7x7), a następnie przejdź do elementów tablicy[2][3]-[2][5], itd. Istnieją bloki danych CHUNK_ROWS, każdy oddzielony 2 int.

Zastanów się teraz, jak odbiorca musi je otrzymać. Załóżmy, że odbiera dane rangi 0. Otrzyma to do elementów tablicy [0][0]-[0][4] -- na razie tak dobrze; ale wtedy otrzyma kolejny blok danych do [1][0]-[1][4], w tablicy o rozmiarze 10x10. To skok ponad 5 elementów. Układ w pamięci jest inny. Więc odbiornik będzie musiał zostać przeniesiony do innego typu Subarray, z którego nadawcy będą wysyłać, ponieważ układ pamięci jest inny.

Więc podczas gdy możesz wysyłać z czegoś, co wygląda tak:

  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);

Otrzymasz coś, co wygląda tak:

  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);

Zwróć uwagę na różnicę w tablicy sizes.

Teraz jesteśmy coraz bliżej. Zauważ, że Twoja linia MPI_Gather zmienia się na coś takiego:
MPI_Gather(DEBUG_CH[0],1,sendsubarray,recvptr,1,recvsubarray,0,MPI_COMM_WORLD);

Było kilka rzeczy, które nie działały w poprzedniej wersji, MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,0,MPI_COMM_WORLD); -- Po pierwsze, zauważ, że odnosisz się do ddd[0], ale dla każdej rangi z wyjątkiem rangi 0, ddd=NULL, i tak to się nie uda. Utwórz więc nową zmienną o nazwie say recvptr, a w rank zero ustaw ją na ddd[0]. (Nie ma znaczenia, gdzie inne procesy myślą, że to jest, ponieważ nie otrzymują.) Również myślę, że nie chcesz otrzymywać CHUNK_ROWS*CHUNK_COLUMS MPI_INTs, bo to by je zapamiętywało, i rozumiem, że chcesz, by były ułożone tak samo jak na zadaniach niewolników, ale w większej tablicy.

Ok, więc teraz do czegoś zmierzamy, ale powyższe nadal nie zadziała, z ciekawego powodu. Dla przykładowych tablic 1D łatwo jest zorientować się, gdzie nth ranges data goes. Sposób, w jaki jest obliczany, polega na znalezieniu zakresu pobieranych danych i uruchomieniu następnego elementu tuż po tym. Ale to nie zadziała tutaj. "Tuż po" koniec danych rangi zero nie jest miejscem, w którym dane rangi jedynej powinny się zaczynać ([0][5]), lecz [4][5] -- element po ostatnim elemencie w randze 0s subarray. Tutaj dane, które otrzymujesz z różnych stopni, nakładają się na siebie! Więc będziemy musieli majstrować z zakresu typów danych, i ręcznie określ, gdzie zaczynają się DANE każdej rangi. Druga to łatwa część; używasz funkcji MPI_Gatherv , gdy musisz ręcznie określić ilość danych z każdego procesora lub miejsce, w którym się one znajdują. Pierwsza jest trudniejsza.

MPI Let 's you specific the lower and upper bounds of a data type -- where, given a piece of memory, the first bit of data for this type would go, and where it "ends", which here only means where the next one could start. (The dane mogą wykraczać poza górną granicę typu, co moim zdaniem czyni te nazwy mylącymi, ale tak to wygląda. Ponieważ będziemy mieli do czynienia z elementami w tablicy int, Sprawmy, aby Rozmiar naszego typu wynosił jeden MPI_INT.

  MPI_Type_create_resized(recvsubarray, 0, 1*sizeof(int), &resizedrevsubarray);
  MPI_Type_commit(&resizedrecvsubarray);

(Uwaga musimy to zrobić tylko dla typu received; od typu send, ponieważ wysyłamy tylko jeden z nich, nie ma to znaczenia).

Teraz, będziemy użyj gatherv, aby określić, gdzie zaczyna się każdy element -- w jednostkach "rozmiaru" tego nowego typu, który jest tylko 1 liczbą całkowitą. Jeśli więc chcemy, aby coś weszło do dużej tablicy w [0][5], przesunięcie od początku dużej tablicy wynosi 5; jeśli chcemy, aby weszło tam w pozycji [5][5], przesunięcie wynosi 55.

Na koniec zauważ, że wszystkie kolektywy zbierają i rozpraszają zakładają, że nawet" mistrz " bierze w tym udział. Najłatwiej to uruchomić, jeśli nawet mistrz ma ich własny kawałek globalnej tablicy.

Więc z tym, następujące działa dla mnie:

#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;
}
 20
Author: Jonathan Dursi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-07 19:35:32