2 Le document de specification :
3 ==============================
5 Globalement le document de specification correspond a
6 l'implementation qui a ete faite avec :
8 . Transport-ParaMEDMEM qui a ete enrichi avec la classe MPI_Access
10 . Presentation-ParaMEDMEM qui a ete enrichi avec la classe
14 La conception correspondant a cette specification est restee
17 . MPI_Access gere pour un ProcessorGroup (IntraCommunicator) :
18 - Les structures MPI_Request et MPI_Status
19 - La valeur des "tags" MPI
20 - Les requetes d'ecritures et de lectures asynchrones
21 - Les communications en "Point a Point" [I]Send, [I]Recv ainsi
23 - A la difference de l'API MPI [I]SendRecv ne concerne qu'un
24 seul et meme "target".
25 - Les controles de communications asynchrones Wait, Test,
26 WaitAll, TestAll, [I]Probe, Cancel et CancelAll.
27 - Comme c'etait demande seules les methodes "utiles" ont ete
29 - Les appels a [I]Send ou a [I]Recv avec des sendbuff/recvbuff
30 de valeur NULL ou avec des sendcount/recvcount de valeur
32 - Les methodes de communications collectives ne sont pas
33 implementees dans MPI_Access.
34 - Les deux methodes "Cancel" concernent soit un IRecv deja
35 soumis soit un message en attente (sans IRecv deja soumis).
36 Elles regroupent les differents appels de l'API MPI
37 necessaires (IProbe, IRecv, Wait, Test_Canceled ...).
39 . MPI_AccessDEC utilise les services de MPI_Access pour un
40 ProcessorGroup (IntraCommunicator) et gere :
41 - Les communications collectives en "Point a Point".
42 (AllToAll[v] synchrone ou asynchrone).
43 - Les temps et l'interpolation
44 - Les [I]Send avec leurs buffers (delete [])
46 - La finalisation des envois et receptions de messages dans
47 le destructeur afin qu'il n'y ait plus de message en attente
48 et afin de liberer les buffers
51 MPI_Access et "tags" (ou "MPITags") :
52 =====================================
54 . Le constructeur permet optionnellement de fixer une plage de tags
55 a utiliser : [BaseTag , MaxTag].
56 Par defaut c'est [ 0 , MPI_TAG_UB], MPI_TAG_UB etant la valeur
57 maximum d'une implementation de MPI (valeur minimum 32767
58 soit 2**15-1). Sur awa avec l'implementation lam MPI_TAG_UB
59 vaut 7353944. La norme MPI specifie que cette valeur doit
60 etre la meme dans les process demarres avec mpirun.
61 Dans le cas de l'usage simultane du meme IntraCommunicator
62 dans un meme process (ou de plusieurs IntraCommunicator
63 d'intersection non nulle) cela peut eviter toute ambiguite
66 . Dans MPI_Access les tags sont constitues de deux parties
67 (#define ModuloTag 10) :
68 + Le dernier digit decimal correspond au MPI_DataType ( 1 pour
69 les messages "temps", 2 pour MPI_INT et 3 pour MPI_DOUBLE)
70 + La valeur des autres digits correspond a une numerotation
71 circulaire des messages.
72 + Un message "temps" et le message de donnees associe ont le
73 meme numero de message (mais des types et donc des tags
76 . Pour un envoi de message d'un process "source" vers un process
77 "target", on dispose de _SendMPITag[target] dans le process
78 source (il contient le dernier "tag" utilise pour l'envoi de
79 messages vers le process target).
80 Et dans le process "target" qui recoit ce message, on dispose
81 de _RecvMPITag[source] (il contient le dernier "tag" utilise
82 pour la reception de messages du process source).
83 Naturellement d'apres la norme MPI les valeurs de ces tags sont
87 MPI_Access et "RequestIds" :
88 ============================
90 . ATTENTION : Dans le document de specification, la distinction
91 n'est pas faite clairement entre les "MPITags" (voir ci-dessus)
92 qui sont un argument des appels a MPI et les "RequestIds" qui
93 ne concernent pas les appels MPI. Ces "RequestIds" figurent
94 en effet sous le nom de tag comme argument d'entree/sortie dans l'API
95 de MPI_Access decrite dans le document de specification. Mais
96 dans l'implementation on a bien le nom RequestId (ou bien
97 RecvRequestId/SendRequestId).
99 . Lors de la soumission d'une requete d'ecriture ou de lecture MPI
100 via MPI_Access, on obtient un identifieur "RequestId".
101 Cet identifieur "RequestId" correspond a une structure RequestStruct
102 de MPI_Access a laquelle on accede avec la map
103 "_MapOfRequestStruct".
104 Cette structure RequestStruct permet de gerer MPI_Request et
105 MPI_Status * de MPI et permet d'obtenir des informations sur
106 la requete : target, send/recv, tag, [a]synchrone, type, outcount.
108 . C'est cet identifieur qui peut etre utilise pour controler une
109 requete asynchrone via MPI_Access : Wait, Test, Probe, etc...
111 . En pratique "RequestId" est simplement un entier de l'intervalle
112 [0 , 2**32-1]. Il y a uniquement un compteur cyclique global
113 aussi bien pour les [I]Send que pour les [I]Recv.
115 . Ces "RequestIds" et leur structures associees facilitent les
116 communications asynchrones.
117 Par exemple on a mpi_access->Wait( int RequestId )
118 au lieu de MPI_Wait(MPI_Request *request, MPI_Status *status)
119 avec gestion de status.
121 . L'API de MPI_Access peut fournir les "SendRequestIds" d'un "target",
122 les "RecvRequestIds" d'un "source" ou bien les "SendRequestIds" de
123 tous les "targets" ou les "RecvRequestIds" de tous les "sources".
124 Cela permet d'eviter leur gestion au niveau de Presentation-ParaMEDMEM.
130 . Comme la classe DEC, il est base sur local_group et distant_group
131 ce qui forme un MPI_union_group et donc un IntraCommunicator.
133 . Il permet de choisir le mode synchrone ou asynchrone (par defaut).
134 Le meme programme peut fonctionner en synchrone ou en asynchrone
135 sans devoir etre modifie.
137 . Il permet de choisir un mode d'interpolation (actuellement
138 uniquement une interpolation lineaire) ou bien un mode sans
139 interpolation (par defaut). Ceci pour les communications collectives.
140 Avec interpolation les communications collectives transmettent et
141 recoivent un message "temps" en plus des donnees.
143 . Il implemente AllToAll[v] en "Point a Point" avec ou sans interpolation.
145 . Il gere les buffers d'envoi de messages. Il les detruit donc
146 lorsqu'ils sont disponibles.
148 . Il cree et utilise MPI_Access.
151 MPI_AccessDEC et la gestion des SendBuffers :
152 =============================================
154 . Comme dans les communications collectives on n'envoie que des
155 parties du meme buffer à chaque process "target", il faut s'assurer
156 en asynchrone que toutes ces parties sont disponibles pour
157 pouvoir liberer le buffer.
159 . On suppose que ces buffers ont ete alloues avec un new double[]
161 . La structure SendBuffStruct permet de conserver l'adresse du buffer
162 et de gerer un compteur de references de ce buffer. Elle comporte
163 aussi MPI_Datatype pour pouvoir faire un delete [] (double *) ...
164 lorsque le compteur est null.
166 . La map _MapOfSendBuffers etablit la correspondance entre chaque
167 RequestId obtenu de MPI_Access->ISend(...) et un SendBuffStruct
168 pour chaque "target" d'une partie du buffer.
170 . Tout cela ne concerne que les envois asynchrones. En synchrone,
171 on detruit senbuf juste apres l'avoir transmis.
174 MPI_AccessDEC et la gestion des RecvBuffers :
175 =============================================
177 S'il n'y a pas d'interpolation, rien de particulier n'est fait.
179 Avec interpolation pour chaque target :
180 ---------------------------------------
181 . On a _TimeMessages[target] qui est un vecteur de TimesMessages.
182 On en a 2 dans notre cas avec une interpolation lineaire qui
183 contiennent le time(t0)/deltatime precedent et le dernier
186 . On a _DataMessages[target] qui est un vecteur de DatasMessages
187 On en a 2 dans notre cas avec une interpolation lineaire qui
188 contiennent les donnees obtenues par Recv au time(t0)/deltatime
189 precedent et au dernier time(t1)/deltatime.
191 . Au temps _t(t*) du processus courrant on effectue l'interpolation
192 entre les valeurs des 2 DatasMessages que l'on rend dans la
193 partie de recvbuf correspondant au target pourvu que t0 < t* <= t1.
195 . Par suite de la difference des "deltatimes" entre process, on
196 peut avoir t0 < t1 < t* auquel cas on aura une extrapolation.
198 . Les vecteurs _OutOfTime, _DataMessagesRecvCount et _DataMessagesType
199 contiennent pour chaque target true si t* > dernier t1, recvcount et
200 MPI_Datatype pour finaliser la gestion des messages a la fin.
203 Etapes des communications collectives de MPI_AccessDEC :
204 ========================================================
206 AllToAll[v] : Les arguments sont les memes que dans MPI sauf MPI_Comm
207 ------------- inutile (deja connu de MPI_AccessDEC et MPI_Access).
209 Si on a un TimeInterpolator, appel de AllToAll[v]Time.
211 Sinon, on appelle CheckSent pour les echanges
212 asynchrones (voir ci-apres) et on appelle SendRecv
213 pour chaque "target".
219 + appelle SendRequestIds de MPI_Access afin d'obtenir tous les
220 RequestIds d'envoi de messages a tous les "targets".
221 + Pour chaque RequestId, appelle Test de MPI_Access pour savoir
222 si le buffer est libre (flag = true). Lorsqu'il s'agit du
223 FinalCheckSent, on appelle Wait au lieu de Test.
224 + Si le buffer est libre, on decremente le compteur de la
225 structure SendBuffStruct obtenue avec _MapOfSendBuffers.
226 (voir MPI_AccessDEC et la gestion des SendBuffers ci-dessus)
227 + Si le compteur est nul on detruit le TimeMessage ou le
228 SendBuffer en fonction du DataType.
229 + Puis on detruit la structure SendBuffStruct avant de supprimer
230 (erase) cet item de _MapOfSendBuffers
233 + On cree un TimeMessage (voir cette structure dans MPI_Access).
234 + Si l'on est en asynchrone on cree deux structures SendBuffStruct
235 aSendTimeStruct et aSendDataStruct que l'on remplit.
236 + On remplit la structure aSendTimeMessage avec time/deltatime du
237 process courant. "deltatime" doit etre nul s'il s'agit du dernier
239 + Puis pour chaque "target", on envoie le TimeMessage et la partie
240 de sendbuf concernee par ce target.
241 + Si l'on est en asynchrone, on incremente le compteur et on ajoute
242 a _MapOfSendBuffers aSendTimeStruct et aSendDataStruct avec les
243 identifieurs SendTimeRequestId et SendDataRequestId recus de
244 MPI_Access->Send(...).
245 + Et enfin si l'on est en synchrone, on detruit les SendMessages.
247 . CheckTime(recvcount , recvtype , target , UntilEnd)
248 + Au depart, on lit le premier "Message-temps" dans
249 &(*_TimeMessages)[target][1] et le premier message de donnees
250 dans le buffer alloue (*_DataMessages)[target][1].
251 + Par convention deltatime des messages temps est nul si c'est le
253 + Boucle while : _t(t*) est le temps courant du processus.
254 "tant que _t(t*) est superieur au temps du "target"
255 (*_TimeMessages)[target][1].time et que
256 (*_TimeMessages)[target][1].deltatime n'est pas nul",
257 ainsi en fin de boucle on aura :
258 _t(t*) <= (*_TimeMessages)[target][1].time avec
259 _t(t*) > (*_TimeMessages)[target][0].time
260 ou bien on aura le dernier message temps du "target".
261 + S'il s'agit de la finalisation des receptions des messages
262 temps et donnees (UntilEnd vaut true), on effectue la
263 boucle jusqu'a ce que l'on trouve
264 (*_TimeMessages)[target][1].deltatime nul.
266 On recopie le dernier message temps dans le message temps
267 precedent et on lit le message temps suivant.
268 On detruit le buffer de donnees du temps precedent.
269 On recopie le pointeur du dernier buffer de donnees dans
271 On alloue un nouveau dernier buffer de donnees
272 (*_DataMessages)[target][1] et on lit les donnees
273 correspondantes dans ce buffer.
274 + Si le temps courant du process est plus grand que le dernier
275 temps (*_TimeMessages)[target][1].time du target, on donne
276 la valeur true a (*_OutOfTime)[target].
277 (*_TimeMessages)[target][1].deltatime est alors nul.
279 . CheckTime + DoRecv + DoInterp
280 + Pour chaque target on appelle CheckTime
281 + Si on a un TimeInterpolator et si le message temps du target
282 n'est pas le premier, on appelle l'interpolateur qui stocke
283 ses resultats dans la partie du buffer de reception qui
284 correspond au "target".
285 + Sinon, on recopie les donnees recues pour ce premier pas de
286 temps dans la partie du buffer de reception qui correspond au
290 Presentation-ParaMEDMEM :
291 =========================
293 . Des modifications mineures ont ete effectuees dans Presentation-ParaMEDMEM
294 afin de pouvoir utiliser ces nouvelles fonctionnalites. Il n'y
295 a surtout pas eu de bouleversement destabilisateur. L'ancien
296 mode de fonctionnement reste naturellement disponible.
298 . Cela repose sur trois nouvelles options creees avec registerOption
299 dans le constructeur de IntersectionDEC :
300 + Asynchronous : true ou false (par defaut)
301 + TimeInterpolation : WithoutTimeInterp (par defaut) ou LinearTimeInterp
302 typedef enum{WithoutTimeInterp,LinearTimeInterp} TimeInterpolationMethod;
303 dans MPI_AccessDEC.hxx
304 + AllToAllMethod : Native (par defaut) ou PointToPoint
305 typedef enum{Native,PointToPoint} AllToAllMethod;
308 . Le choix des options se fait avec le Data Exchange Channel :
309 + ParaMEDMEM::IntersectionDEC dec (*source_group,*target_group);
310 + dec.setOption("Asynchronous",true);
311 + dec.setOption("TimeInterpolation",LinearTimeInterp);
312 + dec.setOption("AllToAllMethod",PointToPoint);
314 . Dans dec.synchronize(),
315 + on cree un objet InterpolationMatrix
316 qui lui-meme cree un objet MxN_Mapping
317 qui lui-meme cree maintenant un objet MPI_AccessDEC
318 + on transmet a MxN_Mapping via l'InterpolationMatrix l'option
319 choisie de AllToAllMethod
320 + on transmet a MPI_AccessDEC les valeurs des options Asynchronous
321 et TimeInterpolation : methodes Asynchronous et
322 SetTimeInterpolator de MPI_AccessDEC.
324 . ParaMEDMEM::IntersectionDEC comporte maintenant une surcharge des
325 methodes recvData() et sendData() :
326 + void IntersectionDEC::recvData( double time ) qui appelle
327 SetTime(time) de MPI_AccessDEC et
329 + void IntersectionDEC::sendData( double time , double deltatime )
331 SetTime(time,deltatime) de MPI_AccessDEC et
334 . recvData() et sendData() de ParaMEDMEM::IntersectionDEC
335 appellent multiply et transposeMultiply de l'InterpolationMatrix
336 qui appellent sendRecv et reverseSendRecv de MxN_Mapping
337 qui appellent comm_interface.allToAllV en mode "Native"
338 ou bien MPI_AccessDEC::AllToAllv en mode "PointToPoint"