mpi: blocage vs non-bloquant

J'ai du mal à comprendre le concept de communication bloquante et de communication non bloquante dans MPI. Quelles sont les différences entre les deux? Quels sont les avantages et les inconvénients?

Merci!

46
demandé sur peaceman 2012-04-04 22:39:45

4 réponses

Bloque la communication se fait à l'aide de MPI_Send() et MPI_Recv(). Ces fonctions ne reviennent pas (c'est-à-dire qu'elles bloquent) jusqu'à ce que la communication soit terminée. En simplifiant quelque peu, cela signifie que le tampon passé à MPI_Send() peut être réutilisé, soit parce que MPI l'a enregistré quelque part, soit parce qu'il a été reçu par la destination. De même, MPI_Recv() renvoie lorsque le tampon de réception a été rempli avec des données valides.

En revanche, la communication non bloquante se fait en utilisant MPI_Isend() et MPI_Irecv(). Ces fonctions reviennent immédiatement (c'est-à-dire qu'elles ne bloquent pas) même si la communication n'est pas encore terminée. Vous devez appeler MPI_Wait() ou MPI_Test() pour voir si la communication est terminée.

La communication bloquante est utilisée quand elle est suffisante, car elle est un peu plus facile à utiliser. La communication Non bloquante est utilisée si nécessaire, par exemple, vous pouvez appeler MPI_Isend(), Faire des calculs, puis faire MPI_Wait(). Cela permet des calculs et la communication se chevauchent, ce qui conduit généralement à une amélioration des performances.

Notez que la communication collective (par exemple, all-reduce) n'est disponible que dans sa version de blocage Jusqu'à MPIv2. IIRC, MPIv3 introduit une communication collective non bloquante.

Un aperçu rapide des modes d'envoi de MPI peut être vu ici.

65
répondu user1202136 2017-12-13 09:15:15

Ce post, bien qu'il soit un peu vieux, mais je soutiens la réponse acceptée. l'instruction "ces fonctions ne reviennent pas tant que la communication n'est pas terminée" est un peu erronée car le blocage des communications ne garantit aucune prise de contact avec les opérations d'envoi et de réception.

La Première chose à savoir, envoyer a quatre modes de de la communication : Standard, Tampon Synchrone et Prêt et chacun d'eux peut être blocage et non-bloquant

Contrairement à envoyer, recevoir n'a qu'un seul mode de et peut être blocage ou non-bloquant .

Avant de continuer, il faut aussi être clair que je mentionne explicitement lequel est MPI_Send \ recv buffer et lequel est system buffer (qui est un tampon local dans chaque processeur appartenant à la bibliothèque MPI utilisée pour déplacer des données entre les rangs d'un groupe de communication)

Blocage COMMUNICATION : Le blocage ne signifie pas que le message a été livré au récepteur / à la destination. Cela signifie simplement que le tampon (envoyer ou recevoir) est disponible pour réutilisation. Pour réutiliser le tampon, il suffit de copier les informations dans une autre zone de mémoire, c'est-à-dire que la bibliothèque peut copier les données du tampon dans son propre emplacement de mémoire dans la bibliothèque, puis, par exemple, MPI_Send peut retourner.

La norme MPI permet de découpler très clairement la mise en mémoire tampon des messages des opérations d'envoi et de réception. Un envoi bloquant peut se terminer dès que le message a été mis en mémoire tampon, même si aucune réception correspondante n'a été postée. Mais dans certains cas, la mise en mémoire tampon des messages peut être coûteuse et la copie directe du tampon d'envoi vers le tampon de réception peut donc être efficace. Par conséquent, MPI standard fournit quatre modes d'envoi différents pour donner à l'utilisateur une certaine liberté dans la sélection du mode d'envoi approprié pour son application. Jetons un coup d'oeil à ce qui se passe dans chaque mode de communication:

1. Mode Standard

Dans le modestandard , Il appartient à la bibliothèque MPI de mettre ou non en mémoire tampon le message sortant. Dans le cas où la bibliothèque décide de mettre en mémoire tampon le message sortant, l'envoi peut se terminer avant même que la réception correspondante ait été invoquée. Dans le cas où la bibliothèque décide de ne pas mettre en mémoire tampon (pour des raisons de performances ou en raison d'une indisponibilité de l'espace tampon), l'envoi ne sera pas renvoyé tant qu'une réception correspondante n'aura pas été validée et que les données du tampon d'envoi n'auront pas été déplacées vers le tampon de réception.

Ainsi MPI_Send en mode standard est non local en ce sens que l'envoi en mode standard peut être démarré si une réception correspondante a été validée ou non et que sa réussite peut dépendre de l'occurrence d'une réception correspondante ( du fait qu'elle dépend de l'implémentation si le message sera mis en mémoire tampon ou non).

La syntaxe pour l'envoi standard est ci-dessous:

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

2. Mode Tampon

Comme dans le mode standard, le mode d'envoi en mémoire tampon peut être démarré indépendamment du fait qu'une réception de correspondance a été validée et que l'envoi peut se terminer avant qu'une réception de correspondance ait été validée. Cependant, la principale différence provient du fait que si l'envoi est regardé et qu'aucune réception correspondante n'est postée, le message sortant doit être mis en mémoire tampon. Remarque Si la réception correspondante est validée, l'envoi tamponné peut se retrouver avec le processeur qui a démarré la réception, mais au cas où il n'y aurait pas de réception, le send in buffered mode doit mettre en mémoire tampon le message sortant pour permettre à l'envoi de se terminer. Dans son intégralité, un envoi tamponné est local . L'allocation de tampon dans ce cas est définie par l'utilisateur et en cas d'espace tampon insuffisant, une erreur se produit.

Syntaxe pour l'envoi du tampon:

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

3. Mode Synchrone

En mode d'envoi synchrone, l'envoi peut être démarré, qu'une réception correspondante ait été validée ou non. Cependant l'envoi ne se terminera avec succès que si un la réception correspondante a été postée et le récepteur a commencé à recevoir le message envoyé par envoi synchrone. L'achèvement de l'envoi synchrone indique non seulement que le tampon dans l'envoi peut être réutilisé, mais aussi le fait que le processus de réception a commencé à recevoir les données. Si l'envoi et la réception bloquent, la communication ne se termine pas à chaque extrémité avant le rendez-vous du processeur communiquant.

Syntaxe pour l'envoi synchrone :

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

4. Prêt En Mode

Contrairement aux trois modes précédents, un envoi en mode prêt ne peut être démarré que si la réception correspondante a déjà été validée. L'achèvement de l'envoi n'indique rien sur la réception correspondante et indique simplement que le tampon d'envoi peut être réutilisé. Un envoi qui utilise le mode prêt a la même sémantique que le mode standard ou un mode synchrone avec les informations supplémentaires sur une réception correspondante. Un programme correct avec un mode de communication prêt peut être remplacé par envoi synchrone ou un envoi standard sans effet sur le résultat en dehors de la différence de performance.

Syntaxe pour l'envoi prêt:

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

Après avoir traversé tous les 4 blocking-send, ils peuvent sembler en principe différents mais selon l'implémentation, la sémantique d'un mode peut être similaire à un autre.

Par exemple MPI_Send en général est un mode de blocage mais selon l'implémentation, si la taille du message n'est pas trop grande, MPI_Send copiera le message sortant de send buffer à System buffer ('ce qui est principalement le cas dans le système moderne)et revenir immédiatement. Regardons un exemple ci-dessous :

//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);
}

Regardons ce qui se passe à chaque rang dans l'exemple ci-dessus

Rang 0 tente d'envoyer au rang 1 et au rang 2, et de recevoir du rang 1 etd 3.

Rang 1 essaie d'envoyer de rang 0 et rang 3 et de ne rien recevoir de tous les autres classements

Rang 2 tente de recevoir depuis le rang 0 et plus tard, faites une opération avec les données reçues dans le recv_buff.

Rang 3 tente d'envoyer au rang 0 et de recevoir du rang 1

Où les débutants sont confus, c'est que le rang 0 envoie au rang 1 mais le rang 1 n'a pas commencé d'opération de réception, d'où la communication devrait bloquer ou bloquer et la deuxième instruction send dans le rang 0 ne devrait pas être exécutée du tout (et c'est ce que la documentation MPI souligne le message sortant sera mis en mémoire tampon ou non). Dans la plupart des systèmes modernes, de tels messages de petite taille (ici la taille est 1) seront facilement mis en mémoire tampon et MPI_Send retournera et exécutera sa prochaine instruction MPI_Send. Par conséquent, dans l'exemple ci-dessus, même si la réception au rang 1 n'est pas démarrée, 1st MPI_Send au rang 0 retournera et exécutera sa prochaine instruction.

Dans une situation hypothétique où le rang 3 commence l'exécution avant le rang 0, il copiera le message sortant dans le premier envoi de la mémoire tampon d'envoi à un tampon système (dans un système moderne ;)), puis commencer à exécuter son instruction receive. Dès que rank 0 termine ses deux instructions d'envoi et commence à exécuter son instruction de réception, les données mises en mémoire tampon dans system by rank 3 sont copiées dans le tampon de réception au rang 0.

Dans le cas où une opération de réception est lancée dans un processeur et qu'aucun envoi correspondant n'est Posté, le processus bloquera jusqu'à ce que le tampon de réception soit rempli avec les données attendues. Dans ce situation un calcul ou une autre communication MPI sera bloqué / arrêté sauf si MPI_Recv est retourné.

Ayant compris les phénomènes de mise en mémoire tampon , Il faut revenir et réfléchir davantage à MPI_Ssend qui a la véritable sémantique d'une communication bloquante. Même si MPI_Ssend copie le message sortant de Send buffer vers un tampon système (qui est à nouveau défini par l'implémentation), il faut noter que MPI_Ssend ne retournera pas à moins que certains l'acceptent (au format de bas niveau) du processus de réception a été reçu par le processeur d'envoi.

Heureusement, MPI a décidé de garder les choses plus faciles pour les utilisateurs en termes de réception et il n'y a qu'une seule réception dans la communication de blocage : MPI_Recv, et peut être utilisé avec l'un des quatre modes d'envoi décrits ci-dessus. Pour MPI_Recv, blocage signifie qui reçoivent des retours seulement après qu'il contient les données dans son tampon. Cela implique que receive ne peut se terminer qu'après le démarrage d'un envoi correspondant mais ne signifie pas si elle peut se terminer avant la fin de l'envoi correspondant.

Ce qui se passe lors de tels appels de blocage est que les calculs sont arrêtés jusqu'à ce que le tampon bloqué soit libéré. Cela conduit généralement au gaspillage des ressources de calcul car Send/Recv copie généralement des données d'un emplacement de mémoire à un autre emplacement de mémoire, tandis que les registres du processeur restent inactifs.

COMMUNICATION NON BLOQUANTE : Pour une Communication non bloquante, l'application crée un demande de communication pour envoyer et / ou recevoir et récupère un handle puis se termine. C'est tout ce qui est nécessaire pour garantir que le processus est exécuté. I. e la bibliothèque MPI est averti que l'opération doit être exécutée.

Pour le côté expéditeur, cela permet de chevaucher le calcul avec la communication.

Côté récepteur, cela permet de superposer une partie de la surcharge de communication, c'est-à-dire de copier le message directement dans l'espace d'adressage de la réception du côté de la demande.

14
répondu gajendra 2018-05-06 21:33:08

En utilisant la communication de blocage, vous devez vous soucier d'envoyer et de recevoir des appels par exemple regardez ce code

 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);
 }

Que se passe-t-il dans ce cas?

  1. le processus 0 envoie x au processus 1 et bloque jusqu'à ce que le processus 1 reçoive X.
  2. le processus 1 envoie y au processus 0 et bloque jusqu'à ce que le processus 0 reçoive y, Mais
  3. le processus 0 est bloqué de telle sorte que le processus 1 Bloque l'infini jusqu'à ce que les deux processus soient tués.
10
répondu peaceman 2014-07-08 11:53:38

C'est facile.

Le non-blocage signifie que le calcul et le transfert de données peuvent avoir lieu en même temps pour un seul processus.

Bien que le blocage signifie, hey buddy, vous devez vous assurer que vous avez déjà terminé le transfert de données, puis revenir pour terminer la commande suivante, ce qui signifie que s'il y a un transfert suivi d'un calcul, le calcul doit être après le succès du transfert.

3
répondu Shaowu 2015-06-23 06:12:37