mpi: bloqueo vs no bloqueo


Estoy teniendo problemas para entender el concepto de bloquear la comunicación y no bloquear la comunicación en MPI. ¿Cuáles son las diferencias entre los dos? ¿Cuáles son las ventajas y desventajas?

Gracias!

Author: peaceman, 2012-04-04

4 answers

El bloqueo de la comunicación se realiza utilizando MPI_Send() y MPI_Recv(). Estas funciones no regresan (es decir, se bloquean) hasta que la comunicación haya terminado. Simplificando un poco, esto significa que el búfer pasado a MPI_Send() puede ser reutilizado, ya sea porque MPI lo guardó en algún lugar, o porque ha sido recibido por el destino. Del mismo modo, MPI_Recv() devuelve cuando el búfer de recepción se ha llenado con datos válidos.

Por el contrario, la comunicación sin bloqueo se realiza utilizando MPI_Isend() y MPI_Irecv(). Estas funciones regresan inmediatamente (es decir, no bloquean) incluso si la comunicación aún no ha terminado. Debes llamar MPI_Wait() o MPI_Test() para ver si la comunicación ha terminado.

El bloqueo de la comunicación se usa cuando es suficiente, ya que es algo más fácil de usar. La comunicación sin bloqueo se utiliza cuando es necesario, por ejemplo, puede llamar a MPI_Isend(), hacer algunos cálculos, luego hacer MPI_Wait(). Esto permite cálculos y la comunicación a la superposición, lo que generalmente conduce a un mejor rendimiento.

Tenga en cuenta que la comunicación colectiva (por ejemplo, all-reduce) solo está disponible en su versión de bloqueo hasta MPIv2. IIRC, MPIv3 introduce la comunicación colectiva sin bloqueo.

Se puede ver un resumen rápido de los modos de envío de MPI aquí.

 65
Author: user1202136,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-13 09:15:15

Este post, aunque es un poco viejo, pero sostengo la respuesta aceptada. la declaración "Estas funciones no regresan hasta que la comunicación haya terminado" es un poco errónea porque el bloqueo de las comunicaciones no garantiza ningún apretón de manos b/w las operaciones de envío y recepción.

Primero hay que saber, enviar tiene cuatro modos de comunicación: Estándar, Búfer, Síncrono y Listo y cada uno de ellos puede ser bloqueo y sin bloqueo

A diferencia de send, receive tiene un solo modo y puede ser blocking o non-blocking.

Antes de continuar, también debe quedar claro que menciono explícitamente cuál es MPI_Send\Recv buffer y cuál es system buffer (que es un búfer local en cada procesador propiedad de la Biblioteca MPI utilizada para mover datos entre filas de un grupo de comunicación)

BLOQUEO COMUNICACIÓN : El bloqueo no significa que el mensaje fue entregado al receptor/destino. Simplemente significa que el búfer (enviar o recibir) está disponible para su reutilización. Para reutilizar el búfer, es suficiente copiar la información a otra área de memoria, es decir, la biblioteca puede copiar los datos del búfer a su propia ubicación de memoria en la biblioteca y luego, por ejemplo, MPI_Send puede devolver.

El estándar MPI deja muy claro desacoplar el almacenamiento en búfer de mensajes de las operaciones de envío y recepción. Un envío de bloqueo puede completarse tan pronto como el mensaje se almacenó en búfer, aunque no se haya publicado ninguna recepción coincidente. Pero en algunos casos el almacenamiento en búfer de mensajes puede ser costoso y, por lo tanto, la copia directa del búfer de envío al búfer de recepción puede ser eficiente. Por lo tanto, el estándar MPI proporciona cuatro modos de envío diferentes para dar al usuario cierta libertad en la selección del modo de envío apropiado para su aplicación. Echemos un vistazo a lo que sucede en cada modo de comunicación :

1. Modo estándar

En el modo estándar, depende de la biblioteca MPI, si almacenar o no el mensaje saliente. En el caso de que la biblioteca decida almacenar en búfer el mensaje saliente, el envío puede completarse incluso antes de que se haya invocado la recepción coincidente. En el caso de que la biblioteca decida no almacenar búfer (por razones de rendimiento o debido a la falta de disponibilidad de espacio en el búfer), el envío no volverá hasta que se haya publicado una recepción coincidente y los datos en el búfer de envío se hayan movido a el buffer de recepción.

Por lo tanto MPI_Send en modo estándar no es local en el sentido de que send en modo estándar se puede iniciar independientemente de si se ha publicado o no una recepción coincidente y su finalización exitosa puede depender de la ocurrencia de una recepción coincidente ( debido al hecho de que depende de la implementación si el mensaje se almacenará en búfer o no) .

La sintaxis para envío estándar es la siguiente:

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, 
             int dest, int tag, MPI_Comm comm)

2. Modo buffered

Como en el modo estándar, el envío en modo búfer puede iniciarse independientemente del hecho de que se haya publicado una recepción coincidente y el envío puede completarse antes de que se haya publicado una recepción coincidente. Sin embargo, la principal diferencia surge del hecho de que si el envío se observa y no se publica ninguna recepción coincidente, el mensaje saliente debe ser almacenado en búfer. Nota si se publica la recepción coincidente, el envío en búfer puede encontrarse felizmente con el procesador que inició la recepción, pero en caso de que no haya recepción, el enviar en modo buffered tiene que almacenar el mensaje saliente para permitir que el envío se complete. En su totalidad, un envío en búfer es local . En este caso, la asignación de búfer está definida por el usuario y, en caso de que el espacio del búfer sea insuficiente, se produce un error.

Sintaxis para buffer send:

int MPI_Bsend(const void *buf, int count, MPI_Datatype datatype,
             int dest, int tag, MPI_Comm comm)

3. Modo Síncrono

En el modo de envío síncrono, el envío se puede iniciar independientemente de que se haya publicado o no una recepción coincidente. Sin embargo, el envío se completará con éxito solo si un se publicó la recepción coincidente y el receptor ha comenzado a recibir el mensaje enviado por envío síncrono. La finalización del envío síncrono no solo indica que el búfer en el envío se puede reutilizar, sino también el hecho de que el proceso de recepción ha comenzado a recibir los datos. Si tanto send como receive están bloqueando, la comunicación no se completa en ninguno de los extremos antes de la cita del procesador comunicante.

Sintaxis para envío síncrono:

int MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
              int tag, MPI_Comm comm)

4. ¿Listos? Mode

A diferencia de los tres modos anteriores, un envío en modo listo solo se puede iniciar si la recepción coincidente ya se ha publicado. La finalización del envío no indica nada sobre la coincidencia de recepción y simplemente indica que el búfer de envío puede ser reutilizado. Un envío que utiliza el modo listo tiene la misma semántica que el modo estándar o un modo síncrono con la información adicional sobre una recepción coincidente. Un programa correcto con un modo de comunicación listo se puede reemplazar con envío síncrono o un envío estándar sin efecto en el resultado, aparte de la diferencia de rendimiento.

Sintaxis para ready send:

int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest, 
              int tag, MPI_Comm comm)

Habiendo pasado por todos los 4 envíos de bloqueo, pueden parecer en principio diferentes, pero dependiendo de la implementación, la semántica de un modo puede ser similar a otra.

Por ejemplo MPI_Send en general es un modo de bloqueo pero dependiendo de la implementación, si el tamaño del mensaje no es demasiado grande, MPI_Send copiará el mensaje saliente del buffer de envío al buffer del sistema ('que es el caso sobre todo en el sistema moderno) y volver inmediatamente. Veamos un ejemplo a continuación :

//assume there are 4 processors numbered from 0 to 3
if(rank==0){
    tag=2;
    MPI_Send(&send_buff1, 1, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
    MPI_Send(&send_buff2, 1, MPI_DOUBLE, 2, tag, MPI_COMM_WORLD);
    MPI_Recv(&recv_buff1, MPI_FLOAT, 3, 5, MPI_COMM_WORLD);
    MPI_Recv(&recv_buff2, MPI_INT, 1, 10, MPI_COMM_WORLD);
}

else if(rank==1){
     tag = 10;
    //receive statement missing, nothing received from proc 0
    MPI_Send(&send_buff3, 1, MPI_INT, 0, tag, MPI_COMM_WORLD);
    MPI_Send(&send_buff3, 1, MPI_INT, 3, tag, MPI_COMM_WORLD);
}

else if(rank==2){
    MPI_Recv(&recv_buff, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
    //do something with receive buffer
}

else{ //if rank == 3
    MPI_Send(send_buff, 1, MPI_FLOAT, 0, 5, MPI_COMM_WORLD);
    MPI_Recv(recv_buff, 1, MPI_INT, 1, 10, MPI_COMM_WORLD);
}

Veamos lo que está sucediendo en cada rango en el ejemplo anterior

Rango 0 está tratando de enviar a rango 1 y rango 2, y recibir desde rango 1 yd 3.

Rango 1 está tratando de enviar a rango 0 y rango 3 y no recibir nada de cualquier otro rango

Rango 2 está tratando de recibir del rango 0 y más tarde hacer alguna operación con los datos recibidos en el recv_buff.

Rango 3 está tratando de enviar al rango 0 y recibir desde el rango 1

Donde los principiantes se confunden es que el rango 0 se envía al rango 1, pero el rango 1 no ha iniciado ninguna operación de recepción, por lo tanto, la comunicación debe bloquear o detener y la segunda instrucción send en el rango 0 no debe ejecutarse en absoluto (y esto es lo que la documentación de MPI enfatiza que el mensaje saliente se almacenará en búfer o no). En la mayor parte del sistema moderno, tales mensajes de tamaños pequeños (aquí el tamaño es 1) se almacenarán fácilmente en búfer y MPI_Send devolverá y ejecutará la siguiente instrucción MPI_Send. Por lo tanto, en el ejemplo anterior, incluso si el receive en el rango 1 no se inicia, 1st MPI_Send en el rango 0 regresará y ejecutará su siguiente instrucción.

En una situación hipotética donde el rango 3 comienza la ejecución antes del rango 0, copiará el mensaje saliente en el primer envío sentencia del búfer de envío a un búfer de sistema (en un sistema moderno;)) y luego comience a ejecutar su sentencia de recepción. Tan pronto como el rango 0 termina sus dos sentencias send y comienza a ejecutar su sentencia receive, los datos almacenados en el búfer system por el rango 3 se copian en el búfer receive en el rango 0.

En caso de que haya una operación de recepción iniciada en un procesador y no se publique ningún envío coincidente, el proceso se bloqueará hasta que el búfer de recepción se llene con los datos que espera. En este situación un cálculo u otra comunicación MPI será bloqueada / detenida a menos que MPI_Recv haya regresado.

Habiendo entendido los fenómenos de buffering , uno debería volver y pensar más en MPI_Ssend que tiene la verdadera semántica de una comunicación de bloqueo. Incluso si MPI_Ssend copia el mensaje saliente de send buffer a un buffer del sistema (que de nuevo está definido por la implementación), se debe tener en cuenta que MPI_Ssend no regresará a menos que algunos lo reconozcan (en formato de bajo nivel) desde el proceso de recepción ha sido recibido por el procesador de envío.

Afortunadamente MPI decidió mantener las cosas más fáciles para los usuarios en términos de recepción y solo hay una recepción en el Bloqueo de la comunicación : MPI_Recv , y se puede usar con cualquiera de los cuatro modos de envío descritos anteriormente. Para MPI_Recv, el bloqueo significa que recibe retornos solo después de que contiene los datos en su búfer. Esto implica que recibir solo puede completarse después de que se haya iniciado un envío coincidente, pero no implica si se puede completar o no antes de que se complete el envío correspondiente.

Lo que sucede durante tales llamadas de bloqueo es que los cálculos se detienen hasta que se libera el búfer bloqueado. Esto generalmente conduce al desperdicio de recursos computacionales, ya que Send/Recv suele copiar datos de una ubicación de memoria a otra ubicación de memoria, mientras que los registros en la cpu permanecen inactivos.

COMUNICACIÓN SIN BLOQUEO : Para la Comunicación sin Bloqueo, la aplicación crea un solicitud de comunicación para enviar y / o recibir y recibe un identificador y luego termina. Eso es todo lo que se necesita para garantizar que el proceso se ejecuta. Es decir, se notifica a la biblioteca MPI que la operación tiene que ser ejecutada.

Para el lado del remitente, esto permite la superposición de cálculos con la comunicación.

Para el lado receptor, esto permite superponer una parte de la sobrecarga de comunicación, es decir, copiar el mensaje directamente en el espacio de direcciones del receptor lado en la aplicación.

 14
Author: gajendra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 21:33:08

Al usar el bloqueo de la comunicación, debe preocuparse por enviar y recibir llamadas, por ejemplo mira este código

 if(rank==0)
 {
     MPI_Send(x to process 1)
     MPI_Recv(y from process 1)
 }
 if(rank==1)
 {
     MPI_Send(y to process 0);
     MPI_Recv(x from process 0);
 }

¿Qué sucede en este caso?

  1. El proceso 0 envía x al proceso 1 y bloquea hasta que el proceso 1 recibe x.
  2. El proceso 1 envía y al proceso 0 y bloquea hasta que el proceso 0 recibe y, pero
  3. el proceso 0 está bloqueado de tal manera que el proceso 1 bloquea el infinito hasta que los dos procesos se matan.
 10
Author: peaceman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-08 11:53:38

Es fácil.

No bloqueo significa que el cálculo y la transferencia de datos pueden ocurrir al mismo tiempo para un solo proceso.

Mientras que el bloqueo significa, hey amigo, usted tiene que asegurarse de que ya ha terminado la transferencia de datos a continuación, volver a terminar el siguiente comando, lo que significa que si hay una transferencia seguida de un cálculo, el cálculo debe ser después del éxito de la transferencia.

 3
Author: Shaowu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-23 06:12:37