2012-02-14 28 views
15

En MPI, estoy haciendo una operación de reducción (mínimo) en un valor. Esto funciona bien, pero ¿cómo tomo el número de procesador del que proviene el mínimo y solicito más información al procesador (o envío de datos adicionales con la operación de reducción)?MPI Obtener procesador con valor mínimo

Respuesta

24

Si no le importa emparejar cada valor localmente con un índice entero (rellenado en este caso con el valor del rango local), puede usar las operaciones integradas MPI_MINLOC or MPI_MAXLOC para reducir; o que es bastante fácil de escribir su propio operador de reducción de MPI para incluir cosas como varios índices, ETCC

actualizado para añadir: Con la MINLOC operadores orden interna o MAXLOC, en lugar de pasar en un solo valor para encontrar el mínimo de , pasas eso más un índice entero. Ese índice puede tener cualquier valor que desee, pero "sigue" el otro valor a lo largo. MPI ha incorporado tipos de datos "pares" - MPI_DOUBLE_INT para un doble + un int, o MPI_2INT para dos entradas, que puede usar.

Supongamos que quiere encontrar el mínimo de una matriz de enteros y en qué tarea de MPI se encuentra. Como es normal, usted encuentra su mínimo local en cada tarea, y realiza la reducción; pero esta vez también sincronizarlo con un entero, en este caso su rango:

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

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

    int rank, size; 
    const int locn=5; 
    int localarr[locn]; 

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

    srand(rank); 
    for (int i=0; i<locn; i++) 
     localarr[i] = rand() % 100; 

    for (int proc=0; proc<size; proc++) { 
     if (rank == proc) { 
      printf("Rank %2d has values: ",rank); 
      for (int i=0; i<locn; i++) 
       printf(" %d ", localarr[i]); 
      printf("\n"); 
     } 
     MPI_Barrier(MPI_COMM_WORLD); 
    } 

    int localres[2]; 
    int globalres[2]; 
    localres[0] = localarr[0]; 
    for (int i=1; i<locn; i++) 
     if (localarr[i] < localres[0]) localres[0] = localarr[i]; 

    localres[1] = rank; 

    MPI_Allreduce(localres, globalres, 1, MPI_2INT, MPI_MINLOC, MPI_COMM_WORLD); 

    if (rank == 0) { 
     printf("Rank %d has lowest value of %d\n", globalres[1], globalres[0]); 
    } 

    MPI_Finalize(); 

    return 0; 
} 

Y corriendo que se obtiene:

$ mpirun -np 5 ./minloc 
Rank 0 has values: 83 86 77 15 93 
Rank 1 has values: 83 86 77 15 93 
Rank 2 has values: 90 19 88 75 61 
Rank 3 has values: 46 85 68 40 25 
Rank 4 has values: 1 83 74 26 63 
Rank 4 has lowest value of 1 

Si el valor que está reduciendo no es un entero, (digamos, un doble), crea una estructura que contiene el valor de reducción y el índice entero, y usa el tipo de datos de par MPI apropiado. (por ejemplo, MPI_DOUBLE_INT).

más actualizada: Ok, sólo por diversión, haciéndolo con nuestra propia operación de reducción y nuestro propio tipo de implementar dos índices:

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

typedef struct dbl_twoindex_struct { 
    double val; 
    int rank; 
    int posn; 
} dbl_twoindex; 


void minloc_dbl_twoindex(void *in, void *inout, int *len, MPI_Datatype *type){ 
    /* ignore type, just trust that it's our dbl_twoindex type */ 
    dbl_twoindex *invals = in; 
    dbl_twoindex *inoutvals = inout; 

    for (int i=0; i<*len; i++) { 
     if (invals[i].val < inoutvals[i].val) { 
      inoutvals[i].val = invals[i].val; 
      inoutvals[i].rank = invals[i].rank; 
      inoutvals[i].posn = invals[i].posn; 
     } 
    } 

    return; 
} 


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

    int rank, size; 
    const int locn=5; 
    double localarr[locn]; 

    dbl_twoindex local, global; 

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

    /* create our new data type */ 
    MPI_Datatype mpi_dbl_twoindex; 
    MPI_Datatype types[3] = { MPI_DOUBLE, MPI_INT, MPI_INT }; 
    MPI_Aint disps[3] = { offsetof(dbl_twoindex, val), 
        offsetof(dbl_twoindex, rank), 
        offsetof(dbl_twoindex, posn), }; 
    int lens[3] = {1,1,1}; 
    MPI_Type_create_struct(3, lens, disps, types, &mpi_dbl_twoindex); 
    MPI_Type_commit(&mpi_dbl_twoindex); 

    /* create our operator */ 
    MPI_Op mpi_minloc_dbl_twoindex; 
    MPI_Op_create(minloc_dbl_twoindex, 1, &mpi_minloc_dbl_twoindex); 

    srand(rank); 
    for (int i=0; i<locn; i++) 
     localarr[i] = 1.*rand()/RAND_MAX; 

    for (int proc=0; proc<size; proc++) { 
     if (rank == proc) { 
      printf("Rank %2d has values: ",rank); 
      for (int i=0; i<locn; i++) 
       printf(" %8.4lf ", localarr[i]); 
      printf("\n"); 
     } 
     MPI_Barrier(MPI_COMM_WORLD); 
    } 

    local.val = localarr[0]; 
    local.posn = 0; 
    for (int i=1; i<locn; i++) 
     if (localarr[i] < local.val) { 
       local.val = localarr[i]; 
       local.posn = i; 
     } 
    local.rank = rank; 

    MPI_Allreduce(&local, &global, 1, mpi_dbl_twoindex, mpi_minloc_dbl_twoindex, MPI_COMM_WORLD); 

    if (rank == 0) { 
     printf("Rank %d has lowest value of %8.4lf in position %d.\n", global.rank, global.val, global.posn); 
    } 

    MPI_Op_free(&mpi_minloc_dbl_twoindex); 
    MPI_Type_free(&mpi_dbl_twoindex); 
    MPI_Finalize(); 

    return 0; 
} 

funcionar, se

$ mpirun -np 5 ./minloc2 
Rank 0 has values: 0.8402 0.3944 0.7831 0.7984 0.9116 
Rank 1 has values: 0.8402 0.3944 0.7831 0.7984 0.9116 
Rank 2 has values: 0.7010 0.8097 0.0888 0.1215 0.3483 
Rank 3 has values: 0.5614 0.2250 0.3931 0.4439 0.2850 
Rank 4 has values: 0.9165 0.1340 0.1912 0.2601 0.2143 
Rank 2 has lowest value of 0.0888 in position 2. 
+0

¿Puede explicar o ejemplificar esto? –

+0

¡Gracias, eso ayudó tanto! ¿Se puede definir algo así como MPI_DOUBLE_2INT para poder enviar más de una clave por doble? –

+0

Creo que para cualquier cosa que no sean los tipos integrados, tendría que escribir su propia operación, pero no sería tan difícil. –