mpi_scatterv - mpigather
Matriz de particiĆ³n MPI en bloques (1)
Quiero dividir la matriz en bloques (no en bandas) 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 8x8, llena de números del 0 al 63. Luego la divido en 4 bloques 4x4, usando MPI_Type_vector y la distribuyo a través de MPI_Send, pero esto requiere un cálculo adicional ya que tengo que calcular las compensaciones para cada bloque en la matriz grande.
Si uso scatter, el primer bloque (arriba a la izquierda) se transfiere OK, pero otros bloques no (desplazamiento incorrecto para el inicio del bloque).
Entonces, ¿es posible transferir bloques de matriz usando MPI_Scatter, o cuál es la mejor manera de realizar 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;
}
Lo que tienes es más o menos "la mejor práctica"; es un poco confuso hasta que te acostumbras.
Dos cosas, sin embargo:
Primero, tenga cuidado con esto: sizeof(MPI_CHAR)
es, supongo, 4 bytes, no 1. MPI_CHAR
es una constante (entera) que describe (a la biblioteca MPI) un personaje. Probablemente desee 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 atrapado usando MPI_Scatterv
, porque no hay una manera fácil de hacer el desplazamiento entre cada bloque del mismo tamaño. Es decir, el primer elemento en el 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 ser capaz de 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