Como enviar / receber em MPI usando todos os processadores

9

Este programa escrito usando C Lagrange e MPI. Eu sou novo no MPI e quero usar todos os processadores para fazer alguns cálculos, incluindo o processo 0. Para aprender este conceito, escrevi o seguinte programa simples. Mas este programa trava na parte inferior depois de receber entrada do processo 0 e não envia os resultados de volta ao processo 0.

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

int main(int argc, char** argv) {    
    MPI_Init(&argc, &argv);
    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    int world_size;
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);

    int number;
    int result;
    if (world_rank == 0) 
    {
        number = -2;
        int i;
        for(i = 0; i < 4; i++)
        {
            MPI_Send(&number, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
        }
        for(i = 0; i < 4; i++)
        {           /*Error: can't get result send by other processos bellow*/
            MPI_Recv(&number, 1, MPI_INT, i, 99, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            printf("Process 0 received number %d from i:%d\n", number, i);
        }
    } 
    /*I want to do this without using an else statement here, so that I can use process 0 to do some calculations as well*/

    MPI_Recv(&number, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 
    printf("*Process %d received number %d from process 0\n",world_rank, number);
    result = world_rank + 1;
    MPI_Send(&result, 1, MPI_INT, 0, 99, MPI_COMM_WORLD);  /* problem happens here when trying to send result back to process 0*/

    MPI_Finalize();
}

Execução e obtenção de resultados:

:$ mpicc test.c -o test
:$ mpirun -np 4 test

*Process 1 received number -2 from process 0
*Process 2 received number -2 from process 0
*Process 3 received number -2 from process 0
/* hangs here and will not continue */

Se puder, mostre-me um exemplo ou edite o código acima, se possível.

    
por D P. 11.10.2016 в 00:46
fonte

2 respostas

1

Eu realmente não entendo o que há de errado em usar 2% de declarações if , em torno do domínio de trabalho. Mas de qualquer forma, aqui está um exemplo do que poderia ser feito.

Eu modifiquei seu código para usar comunicações coletivas, pois elas fazem muito mais sentido do que a série de envio / recebimento que você usou. Como as comunicações iniciais são com um valor uniforme, eu uso um MPI_Bcast() que faz o mesmo em uma única chamada.
Por outro lado, como os valores dos resultados são todos diferentes, uma chamada para MPI_Gather() é perfeitamente apropriada.
Eu também introduzo uma chamada para sleep() apenas para simular que os processos estão funcionando por um tempo, antes de enviar seus resultados.

O código agora é assim:

#include <mpi.h>
#include <stdlib.h>   // for malloc and free
#include <stdio.h>    // for printf
#include <unistd.h>   // for sleep

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

    MPI_Init( &argc, &argv );
    int world_rank;
    MPI_Comm_rank( MPI_COMM_WORLD, &world_rank );
    int world_size;
    MPI_Comm_size( MPI_COMM_WORLD, &world_size );

    // sending the same number to all processes via broadcast from process 0
    int number = world_rank == 0 ? -2 : 0;
    MPI_Bcast( &number, 1, MPI_INT, 0, MPI_COMM_WORLD );
    printf( "Process %d received %d from process 0\n", world_rank, number );

    // Do something usefull here
    sleep( 1 );
    int my_result = world_rank + 1;

    // Now collecting individual results on process 0
    int *results = world_rank == 0 ? malloc( world_size * sizeof( int ) ) : NULL;
    MPI_Gather( &my_result, 1, MPI_INT, results, 1, MPI_INT, 0, MPI_COMM_WORLD );

    // Process 0 prints what it collected
    if ( world_rank == 0 ) {
        for ( int i = 0; i < world_size; i++ ) {
            printf( "Process 0 received result %d from process %d\n", results[i], i );
        }
        free( results );
    }

    MPI_Finalize();

    return 0;
}

Depois de compilá-lo da seguinte forma:

$ mpicc -std=c99 simple_mpi.c -o simple_mpi

Ele é executado e fornece isso:

$ mpiexec -n 4 ./simple_mpi
Process 0 received -2 from process 0
Process 1 received -2 from process 0
Process 3 received -2 from process 0
Process 2 received -2 from process 0
Process 0 received result 1 from process 0
Process 0 received result 2 from process 1
Process 0 received result 3 from process 2
Process 0 received result 4 from process 3
    
por Gilles 14.10.2016 / 00:27
fonte
1

Na verdade, os processos 1-3 estão realmente enviando o resultado de volta ao processador 0. No entanto, o processador 0 está preso na primeira iteração desse loop:

for(i=0; i<4; i++)
{      
    MPI_Recv(&number, 1, MPI_INT, i, 99, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    printf("Process 0 received number %d from i:%d\n", number, i);
}

Na primeira chamada MPI_Recv, o processador 0 irá bloquear a espera para receber uma mensagem de si mesmo com a tag 99, uma mensagem que 0 ainda não enviou.

Geralmente, é uma má idéia para um processador enviar / receber mensagens para si mesmo, especialmente usando o bloqueio de chamadas. 0 já tem o valor na memória. Não precisa enviá-lo para si mesmo.

No entanto, uma solução alternativa é iniciar o loop de recebimento de i=1

for(i=1; i<4; i++)
{           
    MPI_Recv(&number, 1, MPI_INT, i, 99, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    printf("Process 0 received number %d from i:%d\n", number, i);
}

A execução do código agora lhe dará:

Process 1 received number -2 from process 0
Process 2 received number -2 from process 0
Process 3 received number -2 from process 0
Process 0 received number 2 from i:1
Process 0 received number 3 from i:2
Process 0 received number 4 from i:3
Process 0 received number -2 from process 0

Observe que o uso de MPI_Bcast e MPI_Gather, conforme mencionado por Gilles, é uma maneira muito mais eficiente e padrão de distribuição / coleta de dados.

    
por jahed 14.10.2016 / 02:00
fonte