MPI – Impresión en un pedido

10 minutos de lectura

MPI Impresion en un pedido
Κωστας Ιωαννου

Estoy tratando de escribir una función en C donde cada procesador imprima sus propios datos. Esto es lo que tengo:

void print_mesh(int p,int myid,int** U0,int X,int Y){
    int i,m,n;
    for(i=0;i<p;i++){
        if(myid==i){
            printf("myid=%d\n",myid);
            for(n=0;n<X;n++){
                for(m=0;m<Y;m++){
                    printf("%d ",U0[n][m]);
                }
                printf("\n");
            }
        }
        else MPI_Barrier(MPI_COMM_WORLD);
    }
}

No funciona por alguna razón. Las matrices se imprimen todas mezcladas. ¿Tiene alguna idea de por qué esto no funciona? ¿Alguna otra idea que funcione? Si es posible, no quiero enviar toda la matriz en un proceso maestro. Además, no quiero usar funciones precompiladas.

  • Wesley Bland tiene razón; no hay una forma general de hacerlo así debido al almacenamiento en búfer. Utilizo el mismo enfoque que usa aquí en el código de ejemplo con una salida pequeña todo el tiempo y, como cuestión práctica, generalmente funciona bastante bien, pero no hay garantía y ciertamente no funcionará con cantidades considerables de salida (> que una sola búfer de E/S). Lo mejor es usar MPI-IO para escribir en un archivo (por ejemplo, esta respuesta), con la misma advertencia de que es mejor escribir grandes cantidades de datos en formatos binarios.

    – Jonathan Dursi

    10 de julio de 2013 a las 14:35


  • Tenga en cuenta también que está llamando MPI_Barrier dentro de MPI_COMM_WORLD y en cada ronda un puesto en MPI_COMM_WORLD no logra llamarlo. la llamada a MPI_Barrier NO debe estar en el else bloque de la construcción condicional, es decir, eliminar el else palabra clave.

    -Hristo Iliev

    10/07/2013 a las 16:30


No hay forma de garantizar que los mensajes de muchos procesos diferentes llegarán en el orden “correcto” cuando lleguen a otro proceso. Esto es esencialmente lo que está sucediendo aquí.

Aunque no está enviando mensajes explícitamente, cuando imprime algo en la pantalla, debe enviarse al proceso en su sistema local (mpiexec o mpirun) donde se puede imprimir en la pantalla. No hay forma de que MPI sepa cuál es el orden correcto para estos mensajes, por lo que simplemente los imprime a medida que llegan.

Si necesita que sus mensajes se impriman en un orden específico, debe enviarlos todos a un rango que puede imprimirlos en el orden que desee. Siempre que un rango haga toda la impresión, todos los mensajes se ordenarán correctamente.

Debe decirse que probablemente habrá respuestas que pueda encontrar allí que digan que puede poner una nueva línea al final de su cadena o usar flush() para asegurarse de que los búferes se vacíen, pero eso no garantizará el pedido. el extremo remoto por las razones mencionadas anteriormente.

  • +1; fflush(stdout) Sin duda vale la pena intentarlo, pero sí, no hay garantía, y lo que funciona en un sistema puede no funcionar en otro.

    – Jonathan Dursi

    10 de julio de 2013 a las 14:38


Entonces, puedes hacer algo como esto:

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
    MPI_Send(&message, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
    printf("1 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message);
} else {
    int buffer;
    MPI_Status status;
    MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);
    MPI_Get_count(&status, MPI_INT, &buffer);
    if (buffer == 1) {
        printf("2 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message);
        MPI_Recv(&message, buffer, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);
        if (rank + 1 != size) {
            MPI_Send(&message, 1, MPI_INT, ++rank, 0, MPI_COMM_WORLD);
        }
    };
};
MPI_Finalize();

Después de ejecutar:

$ mpirun -n 5 ./a.out 
1 SIZE = 5 RANK = 0 MESSAGE = 999 
2 SIZE = 5 RANK = 1 MESSAGE = 999 
2 SIZE = 5 RANK = 2 MESSAGE = 999 
2 SIZE = 5 RANK = 3 MESSAGE = 999 
2 SIZE = 5 RANK = 4 MESSAGE = 999 

  • ¿Dónde asignas ‘mensaje’?

    – EsencialAnonimato

    28/09/2017 a las 22:34

MPI Impresion en un pedido
johannes blaschke

Me inspiré en la respuesta de Святослав Павленко: usar el bloqueo de comunicaciones MPI para hacer cumplir la salida en serie. Si bien Wesley Bland tiene un punto acerca de que MPI no está diseñado para salida en serie. Entonces, si queremos generar datos, tiene sentido que cada procesador genere datos (sin colisiones). Alternativamente, si el orden de los datos es importante (y no es demasiado grande), el enfoque recomendado es enviarlo todo a la CPU (por ejemplo, rango 0), que luego formatea los datos correctamente.

Para mí, esto parece ser un poco exagerado, especialmente cuando los datos pueden ser cadenas de longitud variable, que con demasiada frecuencia es lo que std::cout << "a=" << some_varible << " b=" << some_other_variable a menudo lo es. Entonces, si queremos una impresión en orden rápida y sucia, podemos aprovechar la respuesta de Святослав Павленко para construir un flujo de salida en serie. Esta solución funciona bien, pero su rendimiento se escala mal con muchas CPU, ¡así que no lo use para la salida de datos!

#include <iostream>
#include <sstream>
#include <mpi.h>

Mantenimiento de la casa de MPI:

int mpi_size;
int mpi_rank;

void init_mpi(int argc, char * argv[]) {
    MPI_Init(& argc, & argv);
    MPI_Comm_size(MPI_COMM_WORLD, & mpi_size);
    MPI_Comm_rank(MPI_COMM_WORLD, & mpi_rank);
}

void finalize_mpi() {
    MPI_Finalize();
}

Clase de propósito general que permite el encadenamiento de mensajes MPI

template<class T, MPI_Datatype MPI_T> class MPIChain{
    // Uses a chained MPI message (T) to coordinate serial execution of code (the content of the message is irrelevant).
    private:
        T message_out; // The messages aren't really used here
        T message_in;
        int size;
        int rank;

    public:
        void next(){
            // Send message to next core (if there is one)
            if(rank + 1 < size) {
            // MPI_Send - Performs a standard-mode blocking send.
            MPI_Send(& message_out, 1, MPI_T, rank + 1, 0, MPI_COMM_WORLD);
            }
        }

        void wait(int & msg_count) {
            // Waits for message to arrive. Message is well-formed if msg_count = 1
            MPI_Status status;

            // MPI_Probe - Blocking test for a message.
            MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status);
            // MPI_Get_count - Gets the number of top level elements.
            MPI_Get_count(& status, MPI_T, & msg_count);

            if(msg_count == 1) {
                // MPI_Recv - Performs a standard-mode blocking receive.
                MPI_Recv(& message_in, msg_count, MPI_T, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status);
            }
        }

        MPIChain(T message_init, int c_rank, int c_size): message_out(message_init), size(c_size), rank(c_rank) {}

        int get_rank() const { return rank;}
        int get_size() const { return size;}
};

Ahora podemos usar nuestro MPIChain class para crear nuestra clase que logra generar un flujo de salida:

class ChainStream : public MPIChain<int, MPI_INT> {
    // Uses the MPIChain class to implement a ostream with a serial operator<< implementation.
    private:
        std::ostream & s_out;

    public:
        ChainStream(std::ostream & os, int c_rank, int c_size)
            : MPIChain<int, MPI_INT>(0, c_rank, c_size), s_out(os) {};

        ChainStream & operator<<(const std::string & os){
            if(this->get_rank() == 0) {
                this->s_out << os;
                // Initiate chain of MPI messages
                this->next();
            } else {
                int msg_count;
                // Wait untill a message arrives (MPIChain::wait uses a blocking test)
                this->wait(msg_count);
                if(msg_count == 1) {
                    // If the message is well-formed (i.e. only one message is recieved): output string
                    this->s_out << os;
                    // Pass onto the next member of the chain (if there is one)
                    this->next();
                }
            }

            // Ensure that the chain is resolved before returning the stream
            MPI_Barrier(MPI_COMM_WORLD);

            // Don't output the ostream! That would break the serial-in-time exuction.
            return *this;
       };
};

Nota la MPI_Barrier al final de operator<<. Esto es para evitar que el código inicie una segunda cadena de salida. A pesar de que esto podría ser movido fuera de la operator<<pensé que lo pondría aquí, ya que se supone que esto es una salida en serie de todos modos….

Poniendolo todo junto:

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

    ChainStream cs(std::cout, mpi_rank, mpi_size);

    std::stringstream str_1, str_2, str_3;
    str_1 << "FIRST:  " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;
    str_2 << "SECOND: " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;
    str_3 << "THIRD:  " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;

    cs << str_1.str() << str_2.str() << str_3.str();
    // Equivalent to:
    //cs << str_1.str();
    //cs << str_2.str();
    //cs << str_3.str();

    finalize_mpi();
}

Tenga en cuenta que estamos concatenando las cadenas str_1, str_2, str_3 antes de les enviamos el ChainStream ejemplo. Normalmente uno haría algo como:

std::cout << "a" << "b" << "c"" << std::endl

pero esto se aplica operator<< de izquierda a derecha, y queremos que las cadenas estén listas para la salida antes de corriendo secuencialmente a través de cada proceso.

g++-7 -O3 -lmpi serial_io_obj.cpp -o serial_io_obj
mpirun -n 10 ./serial_io_obj

Salidas:

FIRST:  MPI_SIZE = 10 RANK = 0
FIRST:  MPI_SIZE = 10 RANK = 1
FIRST:  MPI_SIZE = 10 RANK = 2
FIRST:  MPI_SIZE = 10 RANK = 3
FIRST:  MPI_SIZE = 10 RANK = 4
FIRST:  MPI_SIZE = 10 RANK = 5
FIRST:  MPI_SIZE = 10 RANK = 6
FIRST:  MPI_SIZE = 10 RANK = 7
FIRST:  MPI_SIZE = 10 RANK = 8
FIRST:  MPI_SIZE = 10 RANK = 9
SECOND: MPI_SIZE = 10 RANK = 0
SECOND: MPI_SIZE = 10 RANK = 1
SECOND: MPI_SIZE = 10 RANK = 2
SECOND: MPI_SIZE = 10 RANK = 3
SECOND: MPI_SIZE = 10 RANK = 4
SECOND: MPI_SIZE = 10 RANK = 5
SECOND: MPI_SIZE = 10 RANK = 6
SECOND: MPI_SIZE = 10 RANK = 7
SECOND: MPI_SIZE = 10 RANK = 8
SECOND: MPI_SIZE = 10 RANK = 9
THIRD:  MPI_SIZE = 10 RANK = 0
THIRD:  MPI_SIZE = 10 RANK = 1
THIRD:  MPI_SIZE = 10 RANK = 2
THIRD:  MPI_SIZE = 10 RANK = 3
THIRD:  MPI_SIZE = 10 RANK = 4
THIRD:  MPI_SIZE = 10 RANK = 5
THIRD:  MPI_SIZE = 10 RANK = 6
THIRD:  MPI_SIZE = 10 RANK = 7
THIRD:  MPI_SIZE = 10 RANK = 8
THIRD:  MPI_SIZE = 10 RANK = 9

El estándar MPI no especifica cómo se debe recopilar y almacenar la salida estándar de los diferentes nodos. fflush no ayuda

Si necesita imprimir documentos grandes en orden, probablemente la mejor solución no sea reunirlos todos e imprimirlos a la vez, ya que esto generará tráfico en la red. Una mejor solución es crear algo similar a un anillo virtual donde cada proceso espera un token del proceso anterior, imprime y envía el token al siguiente. Por supuesto, el primer proceso no tiene que esperar, se imprime y se envía al siguiente.

De todos modos, en el caso de una salida realmente grande, donde probablemente no tenga sentido imprimir salidas en video, debe usar MPI-IO como lo sugiere Jonathan Dursi.

Para fines de depuración y desarrollo, puede ejecutar cada proceso en una terminal separada, de modo que se impriman en su propia terminal:

mpirun -np n xterm -hold -e ./output

n: número de procesadores
-hold: mantiene xterm activado después de que finaliza el programa.
output: nombre del ejecutable MPI

1647690068 275 MPI Impresion en un pedido
Alex Guteniev

En C ++, he usado la impresión una vez en un rango dado e imprime una pantalla inconexa ordenada

cout<<"The capabilities of Node "<<node_name<<" are: \n";

cout<<"no of real cores = "<<rcores<< " \n";
cout<<"no of virtual cores = "<<vcores<<" \n";
cout<<"clock speed of Processor = "<<speed<<" MHz \n";
cout<<"RAM size is "<<ramsize<<"\n"<<endl;

la salida es captura de pantalla de salida

¿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