Salome HOME
Revert "Synchronize adm files"
[modules/med.git] / src / ParaMEDMEM / BASICS_JR
1
2 Le document de specification :
3 ==============================
4
5 Globalement le document de specification correspond a
6 l'implementation qui a ete faite avec :
7
8 . Transport-ParaMEDMEM qui a ete enrichi avec la classe MPI_Access
9
10 . Presentation-ParaMEDMEM qui a ete enrichi avec la classe
11   MPI_AccessDEC
12
13
14 La conception correspondant a cette specification est restee
15 la meme :
16
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
22     que [I]SendRecv.
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
28     implementees.
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
31     nulle sont ignores.
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 ...).
38
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 [])
45   - Les [I]Recv
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
49
50
51 MPI_Access et "tags" (ou "MPITags") :
52 =====================================
53
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
64     et aider au debug.
65
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
74     differents).
75
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
84     les memes.
85
86
87 MPI_Access et "RequestIds" :
88 ============================
89
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).
98
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.
107
108 . C'est cet identifieur qui peut etre utilise pour controler une
109     requete asynchrone via MPI_Access : Wait, Test, Probe, etc...
110
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.
114
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.
120
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.
125
126
127 MPI_AccessDEC :
128 ===============
129
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.
132
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.
136
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.
142
143 . Il implemente AllToAll[v] en "Point a Point" avec ou sans interpolation.
144
145 . Il gere les buffers d'envoi de messages. Il les detruit donc
146   lorsqu'ils sont disponibles.
147
148 . Il cree et utilise MPI_Access.
149
150
151 MPI_AccessDEC et la gestion des SendBuffers :
152 =============================================
153
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.
158
159 . On suppose que ces buffers ont ete alloues avec un new double[]
160
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.
165
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.
169
170 . Tout cela ne concerne que les envois asynchrones. En synchrone,
171   on detruit senbuf juste apres l'avoir transmis.
172
173
174 MPI_AccessDEC et la gestion des RecvBuffers :
175 =============================================
176
177 S'il n'y a pas d'interpolation, rien de particulier n'est fait.
178
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
184   time(t1)/deltatime.
185
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.
190
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.
194
195 . Par suite de la difference des "deltatimes" entre process, on
196   peut avoir t0 < t1 < t* auquel cas on aura une extrapolation.
197
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.
201
202
203 Etapes des communications collectives de MPI_AccessDEC :
204 ========================================================
205
206 AllToAll[v] : Les arguments sont les memes que dans MPI sauf MPI_Comm
207 ------------- inutile (deja connu de MPI_AccessDEC et MPI_Access).
208
209               Si on a un TimeInterpolator, appel de AllToAll[v]Time.
210
211               Sinon, on appelle CheckSent pour les echanges
212                 asynchrones (voir ci-apres) et on appelle SendRecv
213                 pour chaque "target".
214
215 AllToAll[v]Time : 
216 -----------------
217
218 . CheckSent() :
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
231
232 . DoSend :
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
238     pas de temps.
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.
246
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
252     dernier.
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.
265   + Dans la boucle :
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
270       le precedent.
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.
278
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
287     "target".
288
289
290 Presentation-ParaMEDMEM :
291 =========================
292
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.
297
298 . Cela repose sur trois nouvelles options creees avec registerOption
299   dans le constructeur de InterpKernelDEC :
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;
306     dans MxN_Mapping.hxx
307
308 . Le choix des options se fait avec le Data Exchange Channel :
309   + ParaMEDMEM::InterpKernelDEC dec (*source_group,*target_group);
310   + dec.setOption("Asynchronous",true);
311   + dec.setOption("TimeInterpolation",LinearTimeInterp);
312   + dec.setOption("AllToAllMethod",PointToPoint);
313
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.
323
324 . ParaMEDMEM::InterpKernelDEC comporte maintenant une surcharge des
325   methodes recvData() et sendData() :
326   + void InterpKernelDEC::recvData( double time ) qui appelle
327     SetTime(time) de MPI_AccessDEC et
328     recvData()
329   + void InterpKernelDEC::sendData( double time , double deltatime )
330     qui appelle 
331     SetTime(time,deltatime) de MPI_AccessDEC et
332     sendData()
333
334 . recvData() et sendData() de ParaMEDMEM::InterpKernelDEC
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"
339