Matriz de partición MPI en bloques

5 minutos de lectura

Quiero dividir la matriz en bloques (no en franjas) y luego distribuir estos bloques usando MPI_Scatter.

Se me ocurrió una solución que funciona, pero creo que está lejos de ser la “mejor práctica”. Tengo una matriz de 8×8, llena de números del 0 al 63. Luego la divido en 4 bloques de 4×4, usando MPI_Type_vector y la distribuyo a través de MPI_Send, pero esto requiere algunos cálculos adicionales ya que tengo que calcular compensaciones para cada bloque en una matriz grande.

Si uso la dispersión, el primer bloque (arriba a la izquierda) se transfiere correctamente, pero otros bloques no (compensación incorrecta para el inicio del bloque).

Entonces, ¿es posible transferir bloques de matriz usando MPI_Scatter, o cuál es la mejor manera de hacer la descomposición deseada?

Este es mi código:

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

#define SIZE 8


int main(void) {

        MPI_Init(NULL, NULL);
        int p, rank;
        MPI_Comm_size(MPI_COMM_WORLD, &p);
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        char i;

        char a[SIZE*SIZE];
        char b[(SIZE/2)*(SIZE/2)];

        MPI_Datatype columntype;
        MPI_Datatype columntype2;

        MPI_Type_vector(4, 4, SIZE, MPI_CHAR, &columntype2);
        MPI_Type_create_resized( columntype2, 0, sizeof(MPI_CHAR), &columntype );
        MPI_Type_commit(&columntype);

        if(rank == 0) {
                for( i = 0; i < SIZE*SIZE; i++) {
                        a[i] = i;
                }

                for(int rec=0; rec < p; rec++) {
                        int offset = (rec%2)*4 + (rec/2)*32;
                      MPI_Send (a+offset, 1, columntype, rec, 0, MPI_COMM_WORLD);
                }
        }
        MPI_Recv (b, 16, MPI_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        //MPI_Scatter(&a, 1, boki, &b, 16, MPI_CHAR , 0, MPI_COMM_WORLD);

        printf("rank= %d  b= \n%d %d %d %d\n%d %d %d %d\n%d %d %d %d\n%d %d %d %d\n", rank, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);

        MPI_Finalize();

        return 0;
}

avatar de usuario
Jonathan Dursi

Lo que tienes es más o menos “mejores prácticas”; es un poco confuso hasta que te acostumbras.

Sin embargo, dos cosas:

Primero, ten cuidado con esto: sizeof(MPI_CHAR) es, supongo, 4 bytes, no 1. MPI_CHAR es una constante (entera) que describe (para la biblioteca MPI) un carácter. probablemente quieras sizeof(char)o SIZE/2*sizeof(char), o cualquier otra cosa conveniente. Pero la idea básica de hacer un cambio de tamaño es correcta.

En segundo lugar, creo que estás atascado usando MPI_Scatterv, sin embargo, porque no hay una manera fácil de hacer que el desplazamiento entre cada bloque tenga el mismo tamaño. Es decir, el primer elemento del primer bloque está en a[0]el segundo está en a[SIZE/2] (salto de tamaño/2), el siguiente está en a[SIZE*(SIZE/2)] (salto de (SIZE-1)*(SIZE/2)). Por lo tanto, debe poder generar manualmente las compensaciones.

Lo siguiente parece funcionar para mí (lo generalicé un poco para que quede más claro cuando “tamaño” significa “número de filas” frente a “número de columnas”, etc.):

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

#define COLS  12
#define ROWS  8

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

    MPI_Init(&argc, &argv);
    int p, rank;
    MPI_Comm_size(MPI_COMM_WORLD, &p);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    char i;

    char a[ROWS*COLS];
    const int NPROWS=2;  /* number of rows in _decomposition_ */
    const int NPCOLS=3;  /* number of cols in _decomposition_ */
    const int BLOCKROWS = ROWS/NPROWS;  /* number of rows in _block_ */
    const int BLOCKCOLS = COLS/NPCOLS; /* number of cols in _block_ */

    if (rank == 0) {
        for (int ii=0; ii<ROWS*COLS; ii++) {
            a[ii] = (char)ii;
        }
    }

    if (p != NPROWS*NPCOLS) {
        fprintf(stderr,"Error: number of PEs %d != %d x %d\n", p, NPROWS, NPCOLS);
        MPI_Finalize();
        exit(-1);
    }
    char b[BLOCKROWS*BLOCKCOLS];
    for (int ii=0; ii<BLOCKROWS*BLOCKCOLS; ii++) b[ii] = 0;

    MPI_Datatype blocktype;
    MPI_Datatype blocktype2;

    MPI_Type_vector(BLOCKROWS, BLOCKCOLS, COLS, MPI_CHAR, &blocktype2);
    MPI_Type_create_resized( blocktype2, 0, sizeof(char), &blocktype);
    MPI_Type_commit(&blocktype);

    int disps[NPROWS*NPCOLS];
    int counts[NPROWS*NPCOLS];
    for (int ii=0; ii<NPROWS; ii++) {
        for (int jj=0; jj<NPCOLS; jj++) {
            disps[ii*NPCOLS+jj] = ii*COLS*BLOCKROWS+jj*BLOCKCOLS;
            counts [ii*NPCOLS+jj] = 1;
        }
    }

    MPI_Scatterv(a, counts, disps, blocktype, b, BLOCKROWS*BLOCKCOLS, MPI_CHAR, 0, MPI_COMM_WORLD);
    /* each proc prints it's "b" out, in order */
    for (int proc=0; proc<p; proc++) {
        if (proc == rank) {
            printf("Rank = %d\n", rank);
            if (rank == 0) {
                printf("Global matrix: \n");
                for (int ii=0; ii<ROWS; ii++) {
                    for (int jj=0; jj<COLS; jj++) {
                        printf("%3d ",(int)a[ii*COLS+jj]);
                    }
                    printf("\n");
                }
            }
            printf("Local Matrix:\n");
            for (int ii=0; ii<BLOCKROWS; ii++) {
                for (int jj=0; jj<BLOCKCOLS; jj++) {
                    printf("%3d ",(int)b[ii*BLOCKCOLS+jj]);
                }
                printf("\n");
            }
            printf("\n");
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }

    MPI_Finalize();

    return 0;
}

Corriendo:

$ mpirun -np 6 ./matrix

Rank = 0
Global matrix: 
  0   1   2   3   4   5   6   7   8   9  10  11 
 12  13  14  15  16  17  18  19  20  21  22  23 
 24  25  26  27  28  29  30  31  32  33  34  35 
 36  37  38  39  40  41  42  43  44  45  46  47 
 48  49  50  51  52  53  54  55  56  57  58  59 
 60  61  62  63  64  65  66  67  68  69  70  71 
 72  73  74  75  76  77  78  79  80  81  82  83 
 84  85  86  87  88  89  90  91  92  93  94  95 
Local Matrix:
  0   1   2   3 
 12  13  14  15 
 24  25  26  27 
 36  37  38  39 

Rank = 1
Local Matrix:
  4   5   6   7 
 16  17  18  19 
 28  29  30  31 
 40  41  42  43 

Rank = 2
Local Matrix:
  8   9  10  11 
 20  21  22  23 
 32  33  34  35 
 44  45  46  47 

Rank = 3
Local Matrix:
 48  49  50  51 
 60  61  62  63 
 72  73  74  75 
 84  85  86  87 

Rank = 4
Local Matrix:
 52  53  54  55 
 64  65  66  67 
 76  77  78  79 
 88  89  90  91 

Rank = 5
Local Matrix:
 56  57  58  59 
 68  69  70  71 
 80  81  82  83 
 92  93  94  95 

  • Lo siento, quería decir con filas o columnas no divisibles por nprows y npcols

    – Antonio

    12 de junio de 2012 a las 11:20

  • Oh; así que eso no es tan malo. Simplemente no lo incluí en este ejemplo porque introduce una gran cantidad de contabilidad que distrae del punto principal que estaba tratando de transmitir sobre MPI_Scatterv. Usaría MPI_Dims_create (digamos) para calcular nprows y npcols de p; y luego calcularía blockrows a partir de blockcols en lugar de tenerlos definidos. (Eso también significa que tendría que asignar dinámicamente esos arreglos locales en lugar de declararlos estáticamente). Si el tamaño no se divide uniformemente entre nprows y npcols, lo más fácil es si el último proceso en filas/columnas toma lo que sobra.

    – Jonathan Dursi

    12 de junio de 2012 a las 12:29

  • Creo que también deberías liberar los tipos MPI; de lo contrario, hay pérdida de memoria.

    -Kevin

    14/03/2015 a las 20:35

  • @JonathanDursi, ¿puedes responder a mi [question] (stackoverflow.com/questions/43737456/…) en el mismo concurso. He probado lo que me sugeriste, pero no me funciona.

    – Siva Srinivas Kolukula

    24 de mayo de 2017 a las 6:28

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad