envoi de blocs de tableau 2D en C en utilisant MPI

Comment envoyer des blocs de tableau 2 - D à différents processeurs? Supposons que la taille du tableau 2D est 400x400 an je veux envoyer des blocs de tailles 100X100 à différents processeurs. L'idée est que chaque processeur effectuera le calcul sur son bloc séparé et enverra son résultat au premier processeur pour le résultat final.

J'utilise MPI dans les programmes C.

45
demandé sur Sundevil 2012-02-14 03:07:34

2 réponses

permettez - moi de commencer par dire que vous généralement ne voulez pas vraiment faire cela-disperser et de recueillir d'énormes morceaux de données à partir d'un certain "maître" processus. Normalement, vous voulez que chaque tâche soit à sa propre pièce du puzzle, et vous devriez viser à ne jamais avoir un processeur besoin d'une "vue globale" de l'ensemble des données; dès que vous en avez besoin, vous limitez l'évolutivité et la taille du problème. Si vous faites cela pour le processus I/O - one lit les données, puis les diffuse, puis les rassemble pour l'écriture, vous voudrez éventuellement regarder dans MPI-IO.

pour en venir à votre question, cependant, MPI a de très belles façons de tirer des données arbitraires de la mémoire, et disperser/recueillir à et à partir d'un ensemble de processeurs. Malheureusement, cela nécessite un bon nombre de concepts MPI - types MPI, extensions, et opérations collectives. Un grand nombre des idées de base sont discutées dans la réponse à cette question -- MPI_Type_create_subarray et MPI_Gather .

mise à Jour - Dans la lumière froide du jour, c'est beaucoup de code et pas beaucoup d'explications. Alors, permettez-moi de m'étendre un peu.

Considérer un 1d entier tableau global de la tâche 0 a que vous voulez distribuer un certain nombre de tâches MPI, afin qu'ils reçoivent chacun un morceau dans leur tableau. Disons que vous avez 4 tâches, et le tableau global est [01234567] . Vous pourriez avoir la tâche 0 envoyer quatre messages (y compris un à lui-même) à distribuez ceci, et quand il est temps de se réassembler, recevez quatre messages pour le regrouper; mais cela prend évidemment beaucoup de temps à un grand nombre de processus. Il existe des routines optimisées pour ces types d'opérations - opérations de dispersion/regroupement. Donc, dans ce cas, vous feriez quelque chose comme ceci:

int global[8];   /* only task 0 has this */
int local[2];    /* everyone has this */
const int root = 0;   /* the processor with the initial global data */

if (rank == root) {
   for (int i=0; i<7; i++) global[i] = i;
}

MPI_Scatter(global, 2, MPI_INT,      /* send everyone 2 ints from global */
            local,  2, MPI_INT,      /* each proc receives 2 ints into local */
            root, MPI_COMM_WORLD);   /* sending process is root, all procs in */
                                     /* MPI_COMM_WORLD participate */

après cela, les données des processeurs ressembleraient à

task 0:  local:[01]  global: [01234567]
task 1:  local:[23]  global: [garbage-]
task 2:  local:[45]  global: [garbage-]
task 3:  local:[67]  global: [garbage-]

C'est-à-dire que l'opération de dispersion prend le Global array et envoie des morceaux 2-int contigus à tous les processeurs.

pour re-assembler le tableau, nous utilisons l'opération MPI_Gather() , qui fonctionne exactement le même mais à l'envers:

for (int i=0; i<2; i++) 
   local[i] = local[i] + rank;

MPI_Gather(local,  2, MPI_INT,      /* everyone sends 2 ints from local */
           global, 2, MPI_INT,      /* root receives 2 ints each proc into global */
           root, MPI_COMM_WORLD);   /* recv'ing process is root, all procs in */
                                    /* MPI_COMM_WORLD participate */

et maintenant les données ressemblent à

task 0:  local:[01]  global: [0134679a]
task 1:  local:[34]  global: [garbage-]
task 2:  local:[67]  global: [garbage-]
task 3:  local:[9a]  global: [garbage-]

Gather rapporte toutes les données en arrière, et voici A est 10 parce que je n'ai pas pensé mon formatage à travers assez soigneusement en commençant cet exemple.

What se produit si le nombre de points de données ne divise pas également le nombre de processus, et nous devons envoyer des nombres différents d'articles à chaque processus? Ensuite, vous avez besoin d'une version généralisée de scatter, MPI_Scatterv() , qui vous permet de spécifier les nombres pour chaque processeur, et les déplacements -- où dans le tableau global ce morceau de données commence. Donc disons que vous aviez un tableau de caractères [abcdefghi] avec 9 caractères, et vous alliez assigner chaque processus deux caractères sauf le dernier, qui a obtenu trois. Il vous faudrait alors

char global[9];   /* only task 0 has this */
char local[3]={'-','-','-'};    /* everyone has this */
int  mynum;                     /* how many items */
const int root = 0;   /* the processor with the initial global data */

if (rank == 0) {
   for (int i=0; i<8; i++) global[i] = 'a'+i;
}

int counts[4] = {2,2,2,3};   /* how many pieces of data everyone has */
mynum = counts[rank];
int displs[4] = {0,2,4,6};   /* the starting point of everyone's data */
                             /* in the global array */

MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] pts from displs[i] */
            MPI_INT,      
            local, mynum, MPI_INT;   /* I'm receiving mynum MPI_INTs into local */
            root, MPI_COMM_WORLD);

Maintenant l'apparence des données

task 0:  local:[ab-]  global: [abcdefghi]
task 1:  local:[cd-]  global: [garbage--]
task 2:  local:[ef-]  global: [garbage--]
task 3:  local:[ghi]  global: [garbage--]

vous avez maintenant utilisé scatterv pour distribuer les quantités irrégulières de données. Le déplacement dans chaque cas est de deux * rank (mesuré en caractères; le déplacement est en unité des types étant envoyés pour une dispersion ou reçus pour un rassemblement; ce n'est généralement pas en octets ou quelque chose) du début du tableau, et les comptes sont {2,2,2,3}. Si elle avait été la premier processeur, nous voulions avoir 3 personnages, nous avons fixé compte={3,2,2,2} et les déplacements auraient été {0,3,5,7}. Gaterver fonctionne à nouveau exactement de la même façon, mais à l'inverse; les tableaux counts et Displays resteraient les mêmes.

maintenant, pour 2D, c'est un peu plus compliqué. Si nous voulons envoyer 2d sublocks d'un tableau 2d, les données que nous envoyons désormais plus est contiguë. Si nous envoyons (disons) des sous-blocs 3x3 d'un tableau 6x6 à 4 processeurs, les données que nous envoyons ont trous dedans:

2D Array

   ---------
   |000|111|
   |000|111|
   |000|111|
   |---+---|
   |222|333|
   |222|333|
   |222|333|
   ---------

Actual layout in memory

   [000111000111000111222333222333222333]

(notez que tout calcul haute performance revient à comprendre la disposition des données en mémoire.)

Si nous voulons envoyer les données, qui sont marquées "1" à la tâche 1, nous avons besoin de sauter trois valeurs, l'envoi de trois valeurs, sautez trois valeurs, l'envoi de trois valeurs, sautez trois valeurs, l'envoi de trois valeurs. Une deuxième complication est lorsque les sous-régions s'arrêtent et commencent; notez que la Région " 1 "ne commence pas là où la région" 0 " s'arrête; après le dernier élément de la région "0", le prochain emplacement dans la mémoire est à mi-chemin à travers la Région "1".

abordons d'abord le premier problème de mise en page - Comment extraire juste les données que nous voulons envoyer. Nous pourrions toujours simplement copier toutes les données de la région " 0 "dans un autre tableau contigu, et envoyer cela; si nous l'avons planifié assez soigneusement, nous pourrions même le faire de telle manière que nous pourrions appeler MPI_Scatter sur les résultats. Mais nous préférerions ne pas avoir à transposer tout notre structure des données de cette façon.

jusqu'à présent, tous les types de données MPI que nous avons utilisés sont des types simples - MPI_INT spécifie (disons) 4 octets dans une rangée. Cependant, MPI vous permet de créer vos propres types de données qui décrivent arbitrairement des mises en page de données complexes en mémoire. Et ce cas -- sous-régions rectangulaires d'un réseau -- est assez commun qu'il y a un appel spécifique pour cela. Pour le 2-dimensionnel cas que nous décrivons ci-dessus,

    MPI_Datatype newtype;
    int sizes[2]    = {6,6};  /* size of global array */
    int subsizes[2] = {3,3};  /* size of sub-region */
    int starts[2]   = {0,0};  /* let's say we're looking at region "0",
                                 which begins at index [0,0] */

    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &newtype);
    MPI_Type_commit(&newtype);

cela crée un type qui indique juste la région "0" du tableau global; nous pourrions envoyer seulement ce morceau de données maintenant à un autre processeur

    MPI_Send(&(global[0][0]), 1, newtype, dest, tag, MPI_COMM_WORLD);  /* region "0" */

et le processus de réception peut recevoir dans un tableau. Notez que le processus de réception, s'il ne reçoit que dans un tableau 3x3, peut et non décrire ce qu'il reçoit comme un type de newtype ; cela ne décrit plus la disposition de la mémoire. Au lieu de cela, il reçoit juste un bloc de 3*3 = 9 entiers:

    MPI_Recv(&(local[0][0]), 3*3, MPI_INT, 0, tag, MPI_COMM_WORLD);

notez que nous pourrions le faire pour d'autres sous-régions, aussi, soit en créant un type différent (avec un tableau différent start ) pour les autres blocs, ou tout simplement en envoyant au point de départ du bloc particulier:

    MPI_Send(&(global[0][3]), 1, newtype, dest, tag, MPI_COMM_WORLD);  /* region "1" */
    MPI_Send(&(global[3][0]), 1, newtype, dest, tag, MPI_COMM_WORLD);  /* region "2" */
    MPI_Send(&(global[3][3]), 1, newtype, dest, tag, MPI_COMM_WORLD);  /* region "3" */

enfin, notez que nous exigeons que global et local soient des morceaux contigus de mémoire ici; c'est-à-dire, &(global[0][0]) et &(local[0][0]) (ou, de manière équivalente, *global et *local pointez vers des morceaux contigus de mémoire 6*6 et 3*3; cela n'est pas garanti par la façon habituelle d'allouer des tableaux Multi-d dynamiques. Il est montré comment, ci-dessous.

maintenant que nous savons comment spécifier les sous-régions, il n'y a plus qu'une chose à discuter avant d'utiliser les opérations de dispersion/cueillette, et c'est la "taille" de ces types. Nous ne pouvions pas seulement utiliser MPI_Scatter() (ou même scatterv) avec ces types encore, parce que ces types ont une étendue de 16 entiers; c'est-à-dire, où ils se terminent est de 16 entiers après qu'ils commencent -- et où ils se terminent ne s'alignent pas bien avec où le bloc suivant commence, donc nous ne pouvons pas juste utiliser la dispersion - il choisirait le mauvais endroit pour commencer à envoyer des données au processeur suivant.

bien sûr, nous pourrions utiliser MPI_Scatterv() et spécifier les déplacements nous - mêmes, et c'est ce que nous allons faire - sauf que les déplacements sont en unités de la taille de type d'envoi, et cela ne nous aide pas non plus; les blocs commencent à des décalages de (0,3,18,21) entiers à partir du début du tableau global, et le fait qu'un bloc termine 16 entiers d'où il commence ne nous permet pas d'exprimer ces déplacements en multiples entiers.

pour traiter cela, MPI vous permet de définir l'étendue du type aux fins de ces calculs. Il ne tronque pas le type; il est juste utilisé pour déterminer où le prochain élément commence donné le dernier élément. Pour les types comme ceux-ci avec des trous dans eux, c'est souvent pratique pour définir l'étendue à être quelque chose de plus petit que la distance en mémoire à la fin réelle du type.

nous pouvons définir la mesure pour être tout ce qui nous convient. Nous pourrions simplement faire l'étendue 1 entier, et ensuite définir les déplacements en unités d'entiers. Dans ce cas, cependant, j'aime définir l'étendue à 3 entiers - la taille d'une sous-rangée - de cette façon, le bloc "1" commence immédiatement après le bloc "0", et le bloc "3" commence immédiatement après le bloc "2". Malheureusement, cela ne fonctionne pas aussi bien en sautant du bloc "2" au bloc "3", mais cela ne peut pas être aidé.

donc pour disperser les sous-blocages dans ce cas, nous ferions ce qui suit:

    MPI_Datatype type, resizedtype;
    int sizes[2]    = {6,6};  /* size of global array */
    int subsizes[2] = {3,3};  /* size of sub-region */
    int starts[2]   = {0,0};  /* let's say we're looking at region "0",
                                 which begins at index [0,0] */

    /* as before */
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type);  
    /* change the extent of the type */
    MPI_Type_create_resized(type, 0, 3*sizeof(int), &resizedtype);
    MPI_Type_commit(&resizedtype);

ici nous avons créé le même type de bloc que précédemment, mais nous l'avons redimensionné; nous n'avons pas changé où le type "commence" (le 0) mais nous avons changé où il "finit" (3 ints). Nous ne l'avons pas mentionné avant, mais le MPI_Type_commit doit être capable d'utiliser le type; mais vous n'avez besoin que de propager le type final que vous utilisez réellement, pas d'étapes intermédiaires. Vous utilisez MPI_Type_free pour libérer le type lorsque vous avez terminé.

donc maintenant, enfin, nous pouvons disperser les blocs: les manipulations de données ci-dessus sont un peu compliquées, mais une fois que c'est fait, la dispersion ressemble juste comme avant:

int counts[4] = {1,1,1,1};   /* how many pieces of data everyone has, in units of blocks */
int displs[4] = {0,1,6,7};   /* the starting point of everyone's data */
                             /* in the global array, in block extents */

MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] types from displs[i] */
            resizedtype,      
            local, 3*3, MPI_INT;   /* I'm receiving 3*3 MPI_INTs into local */
            root, MPI_COMM_WORLD);

et maintenant nous avons fini, après un petit tour de dispersion, rassembler, et les types dérivés MPI.

un exemple de code qui montre à la fois l'opération de collecte et l'opération de dispersion, avec des tableaux de caractères, suit. Exécution du programme:

$ mpirun -n 4 ./gathervarray
Global array is:
0123456789
3456789012
6789012345
9012345678
2345678901
5678901234
8901234567
1234567890
4567890123
7890123456
Local process on rank 0 is:
|01234|
|34567|
|67890|
|90123|
|23456|
Local process on rank 1 is:
|56789|
|89012|
|12345|
|45678|
|78901|
Local process on rank 2 is:
|56789|
|89012|
|12345|
|45678|
|78901|
Local process on rank 3 is:
|01234|
|34567|
|67890|
|90123|
|23456|
Processed grid:
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD

et le code suit.

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

int malloc2dchar(char ***array, int n, int m) {

    /* allocate the n*m contiguous items */
    char *p = (char *)malloc(n*m*sizeof(char));
    if (!p) return -1;

    /* allocate the row pointers into the memory */
    (*array) = (char **)malloc(n*sizeof(char*));
    if (!(*array)) {
       free(p);
       return -1;
    }

    /* set up the pointers into the contiguous memory */
    for (int i=0; i<n; i++)
       (*array)[i] = &(p[i*m]);

    return 0;
}

int free2dchar(char ***array) {
    /* free the memory - the first element of the array is at the start */
    free(&((*array)[0][0]));

    /* free the pointers into the memory */
    free(*array);

    return 0;
}

int main(int argc, char **argv) {
    char **global, **local;
    const int gridsize=10; // size of grid
    const int procgridsize=2;  // size of process grid
    int rank, size;        // rank of current process and no. of processes

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


    if (size != procgridsize*procgridsize) {
        fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize);
        MPI_Abort(MPI_COMM_WORLD,1);
    }


    if (rank == 0) {
        /* fill in the array, and print it */
        malloc2dchar(&global, gridsize, gridsize);
        for (int i=0; i<gridsize; i++) {
            for (int j=0; j<gridsize; j++)
                global[i][j] = '0'+(3*i+j)%10;
        }


        printf("Global array is:\n");
        for (int i=0; i<gridsize; i++) {
            for (int j=0; j<gridsize; j++)
                putchar(global[i][j]);

            printf("\n");
        }
    }

    /* create the local array which we'll process */
    malloc2dchar(&local, gridsize/procgridsize, gridsize/procgridsize);

    /* create a datatype to describe the subarrays of the global array */

    int sizes[2]    = {gridsize, gridsize};         /* global size */
    int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize};     /* local size */
    int starts[2]   = {0,0};                        /* where this one starts */
    MPI_Datatype type, subarrtype;
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_CHAR, &type);
    MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(char), &subarrtype);
    MPI_Type_commit(&subarrtype);

    char *globalptr=NULL;
    if (rank == 0) globalptr = &(global[0][0]);

    /* scatter the array to all processors */
    int sendcounts[procgridsize*procgridsize];
    int displs[procgridsize*procgridsize];

    if (rank == 0) {
        for (int i=0; i<procgridsize*procgridsize; i++) sendcounts[i] = 1;
        int disp = 0;
        for (int i=0; i<procgridsize; i++) {
            for (int j=0; j<procgridsize; j++) {
                displs[i*procgridsize+j] = disp;
                disp += 1;
            }
            disp += ((gridsize/procgridsize)-1)*procgridsize;
        }
    }


    MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]),
                 gridsize*gridsize/(procgridsize*procgridsize), MPI_CHAR,
                 0, MPI_COMM_WORLD);

    /* now all processors print their local data: */

    for (int p=0; p<size; p++) {
        if (rank == p) {
            printf("Local process on rank %d is:\n", rank);
            for (int i=0; i<gridsize/procgridsize; i++) {
                putchar('|');
                for (int j=0; j<gridsize/procgridsize; j++) {
                    putchar(local[i][j]);
                }
                printf("|\n");
            }
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }

    /* now each processor has its local array, and can process it */
    for (int i=0; i<gridsize/procgridsize; i++) {
        for (int j=0; j<gridsize/procgridsize; j++) {
            local[i][j] = 'A' + rank;
        }
    }

    /* it all goes back to process 0 */
    MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize),  MPI_CHAR,
                 globalptr, sendcounts, displs, subarrtype,
                 0, MPI_COMM_WORLD);

    /* don't need the local data anymore */
    free2dchar(&local);

    /* or the MPI data type */
    MPI_Type_free(&subarrtype);

    if (rank == 0) {
        printf("Processed grid:\n");
        for (int i=0; i<gridsize; i++) {
            for (int j=0; j<gridsize; j++) {
                putchar(global[i][j]);
            }
            printf("\n");
        }

        free2dchar(&global);
    }


    MPI_Finalize();

    return 0;
}
116
répondu Jonathan Dursi 2017-05-23 11:46:18

j'ai juste trouvé plus facile de le vérifier de cette façon.

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

/*
 This is a version with integers, rather than char arrays, presented in this
 very good answer: http://stackoverflow.com/a/9271753/2411320
 It will initialize the 2D array, scatter it, increase every value by 1 and then gather it back.
*/

int malloc2D(int ***array, int n, int m) {
    int i;
    /* allocate the n*m contiguous items */
    int *p = malloc(n*m*sizeof(int));
    if (!p) return -1;

    /* allocate the row pointers into the memory */
    (*array) = malloc(n*sizeof(int*));
    if (!(*array)) {
       free(p);
       return -1;
    }

    /* set up the pointers into the contiguous memory */
    for (i=0; i<n; i++)
       (*array)[i] = &(p[i*m]);

    return 0;
}

int free2D(int ***array) {
    /* free the memory - the first element of the array is at the start */
    free(&((*array)[0][0]));

    /* free the pointers into the memory */
    free(*array);

    return 0;
}

int main(int argc, char **argv) {
    int **global, **local;
    const int gridsize=4; // size of grid
    const int procgridsize=2;  // size of process grid
    int rank, size;        // rank of current process and no. of processes
    int i, j, p;

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


    if (size != procgridsize*procgridsize) {
        fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize);
        MPI_Abort(MPI_COMM_WORLD,1);
    }


    if (rank == 0) {
        /* fill in the array, and print it */
        malloc2D(&global, gridsize, gridsize);
        int counter = 0;
        for (i=0; i<gridsize; i++) {
            for (j=0; j<gridsize; j++)
                global[i][j] = ++counter;
        }


        printf("Global array is:\n");
        for (i=0; i<gridsize; i++) {
            for (j=0; j<gridsize; j++) {
                printf("%2d ", global[i][j]);
            }
            printf("\n");
        }
    }
    //return;

    /* create the local array which we'll process */
    malloc2D(&local, gridsize/procgridsize, gridsize/procgridsize);

    /* create a datatype to describe the subarrays of the global array */
    int sizes[2]    = {gridsize, gridsize};         /* global size */
    int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize};     /* local size */
    int starts[2]   = {0,0};                        /* where this one starts */
    MPI_Datatype type, subarrtype;
    MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type);
    MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(int), &subarrtype);
    MPI_Type_commit(&subarrtype);

    int *globalptr=NULL;
    if (rank == 0)
        globalptr = &(global[0][0]);

    /* scatter the array to all processors */
    int sendcounts[procgridsize*procgridsize];
    int displs[procgridsize*procgridsize];

    if (rank == 0) {
        for (i=0; i<procgridsize*procgridsize; i++)
            sendcounts[i] = 1;
        int disp = 0;
        for (i=0; i<procgridsize; i++) {
            for (j=0; j<procgridsize; j++) {
                displs[i*procgridsize+j] = disp;
                disp += 1;
            }
            disp += ((gridsize/procgridsize)-1)*procgridsize;
        }
    }


    MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]),
                 gridsize*gridsize/(procgridsize*procgridsize), MPI_INT,
                 0, MPI_COMM_WORLD);

    /* now all processors print their local data: */

    for (p=0; p<size; p++) {
        if (rank == p) {
            printf("Local process on rank %d is:\n", rank);
            for (i=0; i<gridsize/procgridsize; i++) {
                putchar('|');
                for (j=0; j<gridsize/procgridsize; j++) {
                    printf("%2d ", local[i][j]);
                }
                printf("|\n");
            }
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }

    /* now each processor has its local array, and can process it */
    for (i=0; i<gridsize/procgridsize; i++) {
        for (j=0; j<gridsize/procgridsize; j++) {
            local[i][j] += 1; // increase by one the value
        }
    }

    /* it all goes back to process 0 */
    MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize),  MPI_INT,
                 globalptr, sendcounts, displs, subarrtype,
                 0, MPI_COMM_WORLD);

    /* don't need the local data anymore */
    free2D(&local);

    /* or the MPI data type */
    MPI_Type_free(&subarrtype);

    if (rank == 0) {
        printf("Processed grid:\n");
        for (i=0; i<gridsize; i++) {
            for (j=0; j<gridsize; j++) {
                printf("%2d ", global[i][j]);
            }
            printf("\n");
        }

        free2D(&global);
    }


    MPI_Finalize();

    return 0;
}

sortie:

linux16:>mpicc -o main main.c
linux16:>mpiexec -n 4 main Global array is:
 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15 16
Local process on rank 0 is:
| 1  2 |
| 5  6 |
Local process on rank 1 is:
| 3  4 |
| 7  8 |
Local process on rank 2 is:
| 9 10 |
|13 14 |
Local process on rank 3 is:
|11 12 |
|15 16 |
Processed grid:
 2  3  4  5
 6  7  8  9
10 11 12 13
14 15 16 17
1
répondu gsamaras 2015-12-30 11:57:54