11

(Supponiamo che tutte le matrici siano memorizzate in ordine di riga maggiore). Un esempio che illustra il problema è di distribuire una matrice 10x10 su una griglia 3x3, in modo che la dimensione della sottostringa matrici in ogni nodo sembraBlocchi di matrici di dispersione di dimensioni diverse usando MPI

|-----+-----+-----| 
| 3x3 | 3x3 | 3x4 | 
|-----+-----+-----| 
| 3x3 | 3x3 | 3x4 | 
|-----+-----+-----| 
| 4x3 | 4x3 | 4x4 | 
|-----+-----+-----| 

ho visto molti messaggi su StackOverflow (come sending blocks of 2D array in C using MPI e MPI partition matrix into blocks). Ma si occupano solo di blocchi della stessa dimensione (nel qual caso possiamo semplicemente utilizzare MPI_Type_vector o MPI_Type_create_subarray e una sola chiamata MPI_Scatterv).

Quindi, mi chiedo quale sia il modo più efficiente in MPI di dispersione di una matrice in una griglia di processori in cui ogni processore ha un blocco con una dimensione specificata.

P.S. Ho anche guardato allo MPI_Type_create_darray, ma non sembra che tu possa specificare la dimensione del blocco per ciascun processore.

+0

@Patrick Grazie per i vostri commenti. Penso che "MPI_Type_indexed" non funzioni, perché un singolo tipo può ancora corrispondere solo a un blocco di una certa dimensione. – Roun

risposta

13

Hai per eseguire almeno un ulteriore passaggio in MPI per fare questo.

Il problema è che il più generale delle raccogliere/routine scatter, MPI_Scatterv e MPI_Gatherv, permettono di passare un "vettore" (v) di conteggi/spostamenti, piuttosto che solo un conteggio per Scatter e raccogliere, ma il I tipi sono tutti considerati uguali. Qui, non c'è modo di aggirarlo; i layout di memoria di ogni blocco sono diversi e quindi devono essere trattati da un tipo diverso. Se ci fosse una sola differenza tra i blocchi – alcuni avevano un diverso numero di colonne, o alcuni avevano un numero diverso di righe –, quindi sarebbe sufficiente utilizzare solo conteggi diversi. Ma con diverse colonne e righe, i conteggi non lo faranno; hai davvero bisogno di essere in grado di specificare diversi tipi.

Quindi ciò che si vuole veramente è una routine MPI_Scatterw (dove w significa vv; ad esempio, entrambi i conteggi e tipi sono vettori) spesso discussi ma mai implementati). Ma una cosa del genere non esiste. Il più vicino che si può ottenere è la chiamata molto più generale MPI_Alltoallw, che consente l'invio e la ricezione di dati completamente generali da tutti i lati; come afferma la specifica, "The MPI_ALLTOALLW function generalizes several MPI functions by carefully selecting the input arguments. For example, by making all but one process have sendcounts(i) = 0, this achieves an MPI_SCATTERW function.".

Quindi è possibile farlo con MPI_Alltoallw avendo tutti i processi diversi da quello che ha originariamente tutti i dati (supponiamo che sia il grado 0 qui) inviati a zero tutti i loro conteggi di invio. Tutte le attività avranno anche tutti i loro conteggi di ricezione a zero, tranne che per il primo, la quantità di dati che otterranno dal rango zero.

Per i conteggi di invio del processo 0, dovremo prima definire quattro diversi tipi di tipi (le 4 diverse dimensioni di sotto-righe), quindi i conteggi di invio saranno tutti 1, e l'unica parte che rimane è capire gli spostamenti di invio (che, a differenza scatterv, è qui in unità di byte, perché non c'è nessun tipo singolo si potrebbe usare come unità):

 /* 4 types of blocks - 
     * blocksize*blocksize, blocksize+1*blocksize, blocksize*blocksize+1, blocksize+1*blocksize+1 
     */ 

     MPI_Datatype blocktypes[4]; 
     int subsizes[2]; 
     int starts[2] = {0,0}; 
     for (int i=0; i<2; i++) { 
      subsizes[0] = blocksize+i; 
      for (int j=0; j<2; j++) { 
       subsizes[1] = blocksize+j; 
       MPI_Type_create_subarray(2, globalsizes, subsizes, starts, MPI_ORDER_C, MPI_CHAR, &blocktypes[2*i+j]); 
       MPI_Type_commit(&blocktypes[2*i+j]); 
      } 
     } 

     /* now figure out the displacement and type of each processor's data */ 
     for (int proc=0; proc<size; proc++) { 
      int row, col; 
      rowcol(proc, blocks, &row, &col); 

      sendcounts[proc] = 1; 
      senddispls[proc] = (row*blocksize*globalsizes[1] + col*blocksize)*sizeof(char); 

      int idx = typeIdx(row, col, blocks); 
      sendtypes[proc] = blocktypes[idx]; 
     } 
    } 

    MPI_Alltoallw(globalptr, sendcounts, senddispls, sendtypes, 
        &(localdata[0][0]), recvcounts, recvdispls, recvtypes, 
        MPI_COMM_WORLD); 

E questo funzionerà.

Ma il problema è che la funzione Alltoallw è così completamente generale, che è difficile per le implementazioni fare molto nella linea di ottimizzazione; quindi sarei sorpreso se questo si comportasse come una dispersione di blocchi uguali.

Quindi un altro approccio è fare qualcosa come due fasi di comunicazione.

Il più semplice approccio di questo tipo segue dopo aver constatato che si può quasi ottenere tutti i dati di cui si ha bisogno di andare con una sola MPI_Scatterv() chiamata: nel tuo esempio, se operiamo in quote di un unico vettore colonna con la colonna = 1 e rows = 3 (il numero di righe nella maggior parte dei blocchi del dominio), è possibile distribuire quasi tutti i dati globali agli altri processori. Ciascun processore riceve 3 o 4 di questi vettori, che distribuisce tutti i dati tranne l'ultima riga dell'array globale, che può essere gestito da una semplice seconda dispersione. Sembra così;

/* We're going to be operating mostly in units of a single column of a "normal" sized block. 
* There will need to be two vectors describing these columns; one in the context of the 
* global array, and one in the local results. 
*/ 
MPI_Datatype vec, localvec; 
MPI_Type_vector(blocksize, 1, localsizes[1], MPI_CHAR, &localvec); 
MPI_Type_create_resized(localvec, 0, sizeof(char), &localvec); 
MPI_Type_commit(&localvec); 

MPI_Type_vector(blocksize, 1, globalsizes[1], MPI_CHAR, &vec); 
MPI_Type_create_resized(vec, 0, sizeof(char), &vec); 
MPI_Type_commit(&vec); 

/* The originating process needs to allocate and fill the source array, 
* and then define types defining the array chunks to send, and 
* fill out senddispls, sendcounts (1) and sendtypes. 
*/ 
if (rank == 0) { 
    /* create the vector type which will send one column of a "normal" sized-block */ 
    /* then all processors except those in the last row need to get blocksize*vec or (blocksize+1)*vec */ 
    /* will still have to do something to tidy up the last row of values */ 
    /* we need to make the type have extent of 1 char for scattering */ 
    for (int proc=0; proc<size; proc++) { 
     int row, col; 
     rowcol(proc, blocks, &row, &col); 

     sendcounts[proc] = isLastCol(col, blocks) ? blocksize+1 : blocksize; 
     senddispls[proc] = (row*blocksize*globalsizes[1] + col*blocksize); 
    } 
} 

recvcounts = localsizes[1]; 
MPI_Scatterv(globalptr, sendcounts, senddispls, vec, 
       &(localdata[0][0]), recvcounts, localvec, 0, MPI_COMM_WORLD); 

MPI_Type_free(&localvec); 
if (rank == 0) 
    MPI_Type_free(&vec); 

/* now we need to do one more scatter, scattering just the last row of data 
* just to the processors on the last row. 
* Here we recompute the send counts 
*/ 
if (rank == 0) { 
    for (int proc=0; proc<size; proc++) { 
     int row, col; 
     rowcol(proc, blocks, &row, &col); 
     sendcounts[proc] = 0; 
     senddispls[proc] = 0; 

     if (isLastRow(row,blocks)) { 
      sendcounts[proc] = blocksize; 
      senddispls[proc] = (globalsizes[0]-1)*globalsizes[1]+col*blocksize; 
      if (isLastCol(col,blocks)) 
       sendcounts[proc] += 1; 
     } 
    } 
} 

recvcounts = 0; 
if (isLastRow(myrow, blocks)) { 
    recvcounts = blocksize; 
    if (isLastCol(mycol, blocks)) 
     recvcounts++; 
} 
MPI_Scatterv(globalptr, sendcounts, senddispls, MPI_CHAR, 
       &(localdata[blocksize][0]), recvcounts, MPI_CHAR, 0, MPI_COMM_WORLD); 

Fin qui tutto bene. Ma è un peccato avere la maggior parte dei processori seduti intorno a non fare nulla durante quella finale, "cleanup" scatterv.

Quindi un approccio migliore è quello di distribuire tutte le righe in una prima fase e distribuire i dati tra le colonne in una seconda fase. Qui creiamo nuovi comunicatori, con ciascun processore che appartiene a due nuovi comunicatori: uno che rappresenta altri processori nella stessa riga di blocco e l'altro nella stessa colonna di blocco. Nella prima fase, il processore di origine distribuisce tutte le righe dell'array globale agli altri processori nello stesso comunicatore di colonna, cosa che può essere eseguita in un singolo scatterv. Quindi, quei processori, utilizzando un singolo tipo di dati scatterv e lo stesso tipo di colonne dell'esempio precedente, distribuiscono le colonne a ciascun processore nella stessa riga di blocco. Il risultato è di due abbastanza semplice scatterv di distribuire tutti i dati:

/* create communicators which have processors with the same row or column in them*/ 
MPI_Comm colComm, rowComm; 
MPI_Comm_split(MPI_COMM_WORLD, myrow, rank, &rowComm); 
MPI_Comm_split(MPI_COMM_WORLD, mycol, rank, &colComm); 

/* first, scatter the array by rows, with the processor in column 0 corresponding to each row 
* receiving the data */ 
if (mycol == 0) { 
    int sendcounts[ blocks[0] ]; 
    int senddispls[ blocks[0] ]; 
    senddispls[0] = 0; 

    for (int row=0; row<blocks[0]; row++) { 
     /* each processor gets blocksize rows, each of size globalsizes[1]... */ 
     sendcounts[row] = blocksize*globalsizes[1]; 
     if (row > 0) 
      senddispls[row] = senddispls[row-1] + sendcounts[row-1]; 
    } 
    /* the last processor gets one more */ 
    sendcounts[blocks[0]-1] += globalsizes[1]; 

    /* allocate my rowdata */ 
    rowdata = allocchar2darray(sendcounts[myrow], globalsizes[1]); 

    /* perform the scatter of rows */ 
    MPI_Scatterv(globalptr, sendcounts, senddispls, MPI_CHAR, 
        &(rowdata[0][0]), sendcounts[myrow], MPI_CHAR, 0, colComm); 

} 

/* Now, within each row of processors, we can scatter the columns. 
* We can do this as we did in the previous example; create a vector 
* (and localvector) type and scatter accordingly */ 
int locnrows = blocksize; 
if (isLastRow(myrow, blocks)) 
    locnrows++; 
MPI_Datatype vec, localvec; 
MPI_Type_vector(locnrows, 1, globalsizes[1], MPI_CHAR, &vec); 
MPI_Type_create_resized(vec, 0, sizeof(char), &vec); 
MPI_Type_commit(&vec); 

MPI_Type_vector(locnrows, 1, localsizes[1], MPI_CHAR, &localvec); 
MPI_Type_create_resized(localvec, 0, sizeof(char), &localvec); 
MPI_Type_commit(&localvec); 

int sendcounts[ blocks[1] ]; 
int senddispls[ blocks[1] ]; 
if (mycol == 0) { 
    for (int col=0; col<blocks[1]; col++) { 
     sendcounts[col] = isLastCol(col, blocks) ? blocksize+1 : blocksize; 
     senddispls[col] = col*blocksize; 
    } 
} 
char *rowptr = (mycol == 0) ? &(rowdata[0][0]) : NULL; 

MPI_Scatterv(rowptr, sendcounts, senddispls, vec, 
       &(localdata[0][0]), sendcounts[mycol], localvec, 0, rowComm); 

che è più semplice e dovrebbe essere un relativamente buon equilibrio tra prestazioni e robustezza.

esecuzione di tutti questi tre metodi funziona:

bash-3.2$ mpirun -np 6 ./allmethods alltoall 
Global array: 
abcdefg 
hijklmn 
opqrstu 
vwxyzab 
cdefghi 
jklmnop 
qrstuvw 
xyzabcd 
efghijk 
lmnopqr 
Method - alltoall 

Rank 0: 
abc 
hij 
opq 

Rank 1: 
defg 
klmn 
rstu 

Rank 2: 
vwx 
cde 
jkl 

Rank 3: 
yzab 
fghi 
mnop 

Rank 4: 
qrs 
xyz 
efg 
lmn 

Rank 5: 
tuvw 
abcd 
hijk 
opqr 

bash-3.2$ mpirun -np 6 ./allmethods twophasevecs 
Global array: 
abcdefg 
hijklmn 
opqrstu 
vwxyzab 
cdefghi 
jklmnop 
qrstuvw 
xyzabcd 
efghijk 
lmnopqr 
Method - two phase, vectors, then cleanup 

Rank 0: 
abc 
hij 
opq 

Rank 1: 
defg 
klmn 
rstu 

Rank 2: 
vwx 
cde 
jkl 

Rank 3: 
yzab 
fghi 
mnop 

Rank 4: 
qrs 
xyz 
efg 
lmn 

Rank 5: 
tuvw 
abcd 
hijk 
opqr 
bash-3.2$ mpirun -np 6 ./allmethods twophaserowcol 
Global array: 
abcdefg 
hijklmn 
opqrstu 
vwxyzab 
cdefghi 
jklmnop 
qrstuvw 
xyzabcd 
efghijk 
lmnopqr 
Method - two phase - row, cols 

Rank 0: 
abc 
hij 
opq 

Rank 1: 
defg 
klmn 
rstu 

Rank 2: 
vwx 
cde 
jkl 

Rank 3: 
yzab 
fghi 
mnop 

Rank 4: 
qrs 
xyz 
efg 
lmn 

Rank 5: 
tuvw 
abcd 
hijk 
opqr 

Il codice di attuazione di questi metodi segue; puoi impostare le dimensioni dei blocchi in dimensioni più tipiche per il tuo problema ed eseguire su un numero realistico di processori per avere un'idea di quale sia il migliore per la tua applicazione.

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

/* auxiliary routines, found at end of program */ 

char **allocchar2darray(int n, int m); 
void freechar2darray(char **a); 
void printarray(char **data, int n, int m); 
void rowcol(int rank, const int blocks[2], int *row, int *col); 
int isLastRow(int row, const int blocks[2]); 
int isLastCol(int col, const int blocks[2]); 
int typeIdx(int row, int col, const int blocks[2]); 

/* first method - alltoallw */ 
void alltoall(const int myrow, const int mycol, const int rank, const int size, 
        const int blocks[2], const int blocksize, const int globalsizes[2], const int localsizes[2], 
        const char *const globalptr, char **localdata) { 
    /* 
    * get send and recieve counts ready for alltoallw call. 
    * everyone will be recieving just one block from proc 0; 
    * most procs will be sending nothing to anyone. 
    */ 
    int sendcounts[ size ]; 
    int senddispls[ size ]; 
    MPI_Datatype sendtypes[size]; 
    int recvcounts[ size ]; 
    int recvdispls[ size ]; 
    MPI_Datatype recvtypes[size]; 

    for (int proc=0; proc<size; proc++) { 
     recvcounts[proc] = 0; 
     recvdispls[proc] = 0; 
     recvtypes[proc] = MPI_CHAR; 

     sendcounts[proc] = 0; 
     senddispls[proc] = 0; 
     sendtypes[proc] = MPI_CHAR; 
    } 
    recvcounts[0] = localsizes[0]*localsizes[1]; 
    recvdispls[0] = 0; 


    /* The originating process needs to allocate and fill the source array, 
    * and then define types defining the array chunks to send, and 
    * fill out senddispls, sendcounts (1) and sendtypes. 
    */ 
    if (rank == 0) { 
     /* 4 types of blocks - 
     * blocksize*blocksize, blocksize+1*blocksize, blocksize*blocksize+1, blocksize+1*blocksize+1 
     */ 
     MPI_Datatype blocktypes[4]; 
     int subsizes[2]; 
     int starts[2] = {0,0}; 
     for (int i=0; i<2; i++) { 
      subsizes[0] = blocksize+i; 
      for (int j=0; j<2; j++) { 
       subsizes[1] = blocksize+j; 
       MPI_Type_create_subarray(2, globalsizes, subsizes, starts, MPI_ORDER_C, MPI_CHAR, &blocktypes[2*i+j]); 
       MPI_Type_commit(&blocktypes[2*i+j]); 
      } 
     } 

     /* now figure out the displacement and type of each processor's data */ 
     for (int proc=0; proc<size; proc++) { 
      int row, col; 
      rowcol(proc, blocks, &row, &col); 

      sendcounts[proc] = 1; 
      senddispls[proc] = (row*blocksize*globalsizes[1] + col*blocksize)*sizeof(char); 

      int idx = typeIdx(row, col, blocks); 
      sendtypes[proc] = blocktypes[idx]; 
     } 
    } 

    MPI_Alltoallw(globalptr, sendcounts, senddispls, sendtypes, 
        &(localdata[0][0]), recvcounts, recvdispls, recvtypes, 
        MPI_COMM_WORLD); 
} 


/* second method: distribute almost all data using colums of size blocksize, 
* then clean up the last row with another scatterv */ 

void twophasevecs(const int myrow, const int mycol, const int rank, const int size, 
        const int blocks[2], const int blocksize, const int globalsizes[2], const int localsizes[2], 
        const char *const globalptr, char **localdata) { 
    int sendcounts[ size ]; 
    int senddispls[ size ]; 
    int recvcounts; 

    for (int proc=0; proc<size; proc++) { 
     sendcounts[proc] = 0; 
     senddispls[proc] = 0; 
    } 

    /* We're going to be operating mostly in units of a single column of a "normal" sized block. 
    * There will need to be two vectors describing these columns; one in the context of the 
    * global array, and one in the local results. 
    */ 
    MPI_Datatype vec, localvec; 
    MPI_Type_vector(blocksize, 1, localsizes[1], MPI_CHAR, &localvec); 
    MPI_Type_create_resized(localvec, 0, sizeof(char), &localvec); 
    MPI_Type_commit(&localvec); 

    MPI_Type_vector(blocksize, 1, globalsizes[1], MPI_CHAR, &vec); 
    MPI_Type_create_resized(vec, 0, sizeof(char), &vec); 
    MPI_Type_commit(&vec); 

    /* The originating process needs to allocate and fill the source array, 
    * and then define types defining the array chunks to send, and 
    * fill out senddispls, sendcounts (1) and sendtypes. 
    */ 
    if (rank == 0) { 
     /* create the vector type which will send one column of a "normal" sized-block */ 
     /* then all processors except those in the last row need to get blocksize*vec or (blocksize+1)*vec */ 
     /* will still have to do something to tidy up the last row of values */ 
     /* we need to make the type have extent of 1 char for scattering */ 
     for (int proc=0; proc<size; proc++) { 
      int row, col; 
      rowcol(proc, blocks, &row, &col); 

      sendcounts[proc] = isLastCol(col, blocks) ? blocksize+1 : blocksize; 
      senddispls[proc] = (row*blocksize*globalsizes[1] + col*blocksize); 
     } 
    } 

    recvcounts = localsizes[1]; 
    MPI_Scatterv(globalptr, sendcounts, senddispls, vec, 
        &(localdata[0][0]), recvcounts, localvec, 0, MPI_COMM_WORLD); 

    MPI_Type_free(&localvec); 
    if (rank == 0) 
     MPI_Type_free(&vec); 

    /* now we need to do one more scatter, scattering just the last row of data 
    * just to the processors on the last row. 
    * Here we recompute the sendcounts 
    */ 
    if (rank == 0) { 
     for (int proc=0; proc<size; proc++) { 
      int row, col; 
      rowcol(proc, blocks, &row, &col); 
      sendcounts[proc] = 0; 
      senddispls[proc] = 0; 

      if (isLastRow(row,blocks)) { 
       sendcounts[proc] = blocksize; 
       senddispls[proc] = (globalsizes[0]-1)*globalsizes[1]+col*blocksize; 
       if (isLastCol(col,blocks)) 
        sendcounts[proc] += 1; 
      } 
     } 
    } 

    recvcounts = 0; 
    if (isLastRow(myrow, blocks)) { 
     recvcounts = blocksize; 
     if (isLastCol(mycol, blocks)) 
      recvcounts++; 
    } 
    MPI_Scatterv(globalptr, sendcounts, senddispls, MPI_CHAR, 
        &(localdata[blocksize][0]), recvcounts, MPI_CHAR, 0, MPI_COMM_WORLD); 
} 
/* third method: first distribute rows, then columns, each with a single scatterv */ 

void twophaseRowCol(const int myrow, const int mycol, const int rank, const int size, 
        const int blocks[2], const int blocksize, const int globalsizes[2], const int localsizes[2], 
        const char *const globalptr, char **localdata) { 
    char **rowdata ; 

    /* create communicators which have processors with the same row or column in them*/ 
    MPI_Comm colComm, rowComm; 
    MPI_Comm_split(MPI_COMM_WORLD, myrow, rank, &rowComm); 
    MPI_Comm_split(MPI_COMM_WORLD, mycol, rank, &colComm); 

    /* first, scatter the array by rows, with the processor in column 0 corresponding to each row 
    * receiving the data */ 
    if (mycol == 0) { 
     int sendcounts[ blocks[0] ]; 
     int senddispls[ blocks[0] ]; 
     senddispls[0] = 0; 

     for (int row=0; row<blocks[0]; row++) { 
      /* each processor gets blocksize rows, each of size globalsizes[1]... */ 
      sendcounts[row] = blocksize*globalsizes[1]; 
      if (row > 0) 
       senddispls[row] = senddispls[row-1] + sendcounts[row-1]; 
     } 
     /* the last processor gets one more */ 
     sendcounts[blocks[0]-1] += globalsizes[1]; 

     /* allocate my rowdata */ 
     rowdata = allocchar2darray(sendcounts[myrow], globalsizes[1]); 

     /* perform the scatter of rows */ 
     MPI_Scatterv(globalptr, sendcounts, senddispls, MPI_CHAR, 
         &(rowdata[0][0]), sendcounts[myrow], MPI_CHAR, 0, colComm); 

    } 

    /* Now, within each row of processors, we can scatter the columns. 
    * We can do this as we did in the previous example; create a vector 
    * (and localvector) type and scatter accordingly */ 
    int locnrows = blocksize; 
    if (isLastRow(myrow, blocks)) 
     locnrows++; 

    MPI_Datatype vec, localvec; 
    MPI_Type_vector(locnrows, 1, globalsizes[1], MPI_CHAR, &vec); 
    MPI_Type_create_resized(vec, 0, sizeof(char), &vec); 
    MPI_Type_commit(&vec); 

    MPI_Type_vector(locnrows, 1, localsizes[1], MPI_CHAR, &localvec); 
    MPI_Type_create_resized(localvec, 0, sizeof(char), &localvec); 
    MPI_Type_commit(&localvec); 

    int sendcounts[ blocks[1] ]; 
    int senddispls[ blocks[1] ]; 
    if (mycol == 0) { 
     for (int col=0; col<blocks[1]; col++) { 
      sendcounts[col] = isLastCol(col, blocks) ? blocksize+1 : blocksize; 
      senddispls[col] = col*blocksize; 
     } 
    } 
    char *rowptr = (mycol == 0) ? &(rowdata[0][0]) : NULL; 

    MPI_Scatterv(rowptr, sendcounts, senddispls, vec, 
        &(localdata[0][0]), sendcounts[mycol], localvec, 0, rowComm); 

    MPI_Type_free(&localvec); 
    MPI_Type_free(&vec); 

    if (mycol == 0) 
     freechar2darray(rowdata); 

    MPI_Comm_free(&rowComm); 
    MPI_Comm_free(&colComm); 
} 

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

    int rank, size; 
    int blocks[2] = {0,0}; 
    const int blocksize=3; 
    int globalsizes[2], localsizes[2]; 
    char **globaldata; 
    char *globalptr = NULL; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 

    if (rank == 0 && argc < 2) { 
     fprintf(stderr,"Usage: %s method\n Where method is one of: alltoall, twophasevecs, twophaserowcol\n", argv[0]); 
     MPI_Abort(MPI_COMM_WORLD,1); 
    } 

    /* calculate sizes for a 2d grid of processors */ 
    MPI_Dims_create(size, 2, blocks); 

    int myrow, mycol; 
    rowcol(rank, blocks, &myrow, &mycol); 

    /* create array sizes so that last block has 1 too many rows/cols */ 
    globalsizes[0] = blocks[0]*blocksize+1; 
    globalsizes[1] = blocks[1]*blocksize+1; 
    if (rank == 0) { 
     globaldata = allocchar2darray(globalsizes[0], globalsizes[1]); 
     globalptr = &(globaldata[0][0]); 
     for (int i=0; i<globalsizes[0]; i++) 
      for (int j=0; j<globalsizes[1]; j++) 
       globaldata[i][j] = 'a'+(i*globalsizes[1] + j)%26; 

     printf("Global array: \n"); 
     printarray(globaldata, globalsizes[0], globalsizes[1]); 
    } 

    /* the local chunk we'll be receiving */ 
    localsizes[0] = blocksize; localsizes[1] = blocksize; 
    if (isLastRow(myrow,blocks)) localsizes[0]++; 
    if (isLastCol(mycol,blocks)) localsizes[1]++; 
    char **localdata = allocchar2darray(localsizes[0],localsizes[1]); 

    if (!strcasecmp(argv[1], "alltoall")) { 
     if (rank == 0) printf("Method - alltoall\n"); 
     alltoall(myrow, mycol, rank, size, blocks, blocksize, globalsizes, localsizes, globalptr, localdata); 
    } else if (!strcasecmp(argv[1],"twophasevecs")) { 
     if (rank == 0) printf("Method - two phase, vectors, then cleanup\n"); 
     twophasevecs(myrow, mycol, rank, size, blocks, blocksize, globalsizes, localsizes, globalptr, localdata); 
    } else { 
     if (rank == 0) printf("Method - two phase - row, cols\n"); 
     twophaseRowCol(myrow, mycol, rank, size, blocks, blocksize, globalsizes, localsizes, globalptr, localdata); 
    } 

    for (int proc=0; proc<size; proc++) { 
     if (proc == rank) { 
      printf("\nRank %d:\n", proc); 
      printarray(localdata, localsizes[0], localsizes[1]); 
     } 
     MPI_Barrier(MPI_COMM_WORLD);    
    } 

    freechar2darray(localdata); 
    if (rank == 0) 
     freechar2darray(globaldata); 

    MPI_Finalize(); 

    return 0; 
} 

char **allocchar2darray(int n, int m) { 
    char **ptrs = malloc(n*sizeof(char *)); 
    ptrs[0] = malloc(n*m*sizeof(char)); 
    for (int i=0; i<n*m; i++) 
     ptrs[0][i]='.'; 

    for (int i=1; i<n; i++) 
     ptrs[i] = ptrs[i-1] + m; 

    return ptrs; 
} 

void freechar2darray(char **a) { 
    free(a[0]); 
    free(a); 
} 

void printarray(char **data, int n, int m) { 
    for (int i=0; i<n; i++) { 
     for (int j=0; j<m; j++) 
      putchar(data[i][j]); 
     putchar('\n'); 
    } 
} 

void rowcol(int rank, const int blocks[2], int *row, int *col) { 
    *row = rank/blocks[1]; 
    *col = rank % blocks[1]; 
} 

int isLastRow(int row, const int blocks[2]) { 
    return (row == blocks[0]-1); 
} 

int isLastCol(int col, const int blocks[2]) { 
    return (col == blocks[1]-1); 
} 

int typeIdx(int row, int col, const int blocks[2]) { 
    int lastrow = (row == blocks[0]-1); 
    int lastcol = (col == blocks[1]-1); 

    return lastrow*2 + lastcol; 
} 
+0

Grazie, è grandioso. Ho eseguito alcuni test, per distribuire una matrice 4000 * 4000 su 32 processi, il terzo metodo (che immagino sia il migliore) richiede circa 5 volte di più di una singola dispersione. Qualche idea del perché? – Flash

+0

Sospetto che il terzo metodo sarebbe il migliore in termini di ridimensionamento, infine, rispetto al primo, ma probabilmente sarebbe necessario eseguire molti processori per vincere sul fatto che ci sono due operazioni abbastanza grandi.(E mi aspetto che queste due operazioni siano sempre più costose di una singola dispersione di dimensioni uniformi). Se fosse vero, il secondo metodo avrebbe probabilmente prestazioni simili e il primo potrebbe essere più veloce? È un po 'difficile da indovinare e confesso che non sono riuscito a fare un test di ridimensionamento adeguato. –

+0

Ok, non ho implementato gli altri due in modo che potessero fare di meglio. Avrei pensato che il terzo metodo non poteva essere peggiore di un fattore 2 dal momento che ogni passo non è peggiore della dispersione globale. Forse c'è un sovraccarico nel creare i tipi ecc? – Flash

0

Non so se questo si applica a te, ma mi ha aiutato in passato, quindi potrebbe essere utile per gli altri.

La mia risposta si applica nel contesto di parallele IO. Il fatto è che, se si conosce l'accesso non si sovrappongono, si può con successo scrivere/leggere anche con dimensioni variabili utilizzando MPI_COMM_SELF

Un pezzo di codice che uso tutti i giorni contiene:

MPI_File fh; 
MPI_File_open(MPI_COMM_SELF, path.c_str(), MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &fh); 

// Lot of computation to get the size right 

MPI_Datatype filetype; 
MPI_Type_create_subarray(gsizes.size(), &gsizes[0], &lsizes[0], &offset[0], MPI_ORDER_C, MPI_FLOAT, &filetype); 
MPI_Type_commit(&filetype); 

MPI_File_set_view(fh, 0, MPI_FLOAT, filetype, "native", MPI_INFO_NULL); 
MPI_File_write(fh, &block->field[0], block->field.size(), MPI_FLOAT, MPI_STATUS_IGNORE); 
MPI_File_close(&fh);  
Problemi correlati