• Aucun résultat trouvé

dΦ dχ = 1 2 X i          P jAT ijd dD(sT i,mj ) dT t −P jAT ijdD(sdTT i,mj) t .dD(sT i,mj) +P jAij.dD(sT i,mj) dT t P kAik.dD(sT i,mk)           Dans le cas o`u les points sont ind´ependants, et Σm = 0 on obtient finalement :

G = P iGi.R.Σ−1s isi.Rt.Gt i +P jGj−1m jmj.Gt j Gi = 12 ∂sT i ∂T t .        P jAT ij(HsT iD(sT i,mj)) −P jAT ijdD(sT i,mj) dsT i t .dD(sT i,mj) dsT i +P jAij.dD(sT i,mj) dsT i t P kAik.dD(sT i,mk) dsT i         Gj = 12 ∂sT i ∂T t .        P jAT ij(HsT iD(sT i,mj)) −P jAT ijdD(sdsT i,mj) T i t .dD(sT i,mj) dsT i +P jAij.dD(sT i,mj) dsT i t P kAkj.dD(sT k,mj) dsT k        

Nous reviendrons sur cette formule de pr´ediction de l’incertitude adapt´ee `a l’EM dans le cadre de la pr´ediction th´eorique de la pr´ecision du recalage sur les surfaces, section 5.4.

4.6 Parall´elisation

4.6.1 Introduction

Lorsque l’on dispose de plusieurs processeurs, il peut ˆetre int´eressant de r´epartir les calculs entre eux, afin de profiter de toute la puissance disponible et acc´el´erer ainsi le recalage. Ceci peut arriver dans deux cas de figures diff´erents :

au-trefois extrˆemement ch`eres et sp´ecialis´ees, sont devenues courantes, `a l’image des PC bi-pros disponibles chez Epidaure. Il faut savoir que sur ce type de PC, un pro-gramme normal ne sera ex´ecut´e que sur un seul processeur. L’int´erˆet est de pouvoir faire tourner plusieurs programmes en mˆeme temps. Cependant, `a l’aide d’une pro-grammation sp´ecifique (dite “par processus all´eg´es”, “multi-thread” en anglais), on pourra adapter un programme pour qu’il se divise en plusieurs processus qui seront r´epartis sur les diff´erents processeurs. Ces diff´erents processus pourront communi-quer rapidement entre eux et partager la mˆeme m´emoire mais il faudra alors faire attention qu’il n’´ecrivent pas au mˆeme endroit en mˆeme temps, ce qui peut rendre la parall´elisation assez d´elicate.

– Les r´eseaux de machines : lorsque plusieurs machines sont connect´ees entre elles via un r´eseau, on peut les faire communiquer pour qu’elles se r´epartissent le travail. Le partage de m´emoire sera en revanche impossible, et les communications assez lentes (de l’ordre de 0.1 ms).

Nous ne nous consacrerons ici qu’au deuxi`eme cas, plus g´en´eral. En effet, on pourra toujours lancer deux instances du programme sur un bi-pro et les faire communiquer comme si elles ´etaient sur des machines diff´erentes. Les programmes seront alors r´epartis sur les deux processeurs par le syst`eme d’exploitation, r´ealisant ainsi la parall´elisation. Ils ne pourront en revanche pas partager la m´emoire (on aura donc deux copies des mˆemes donn´ees) et les communications seront plus lentes. Ceci ne sera heureusement pas gˆenant dans notre cas, car le recalage utilise peu de m´emoire (quelques m´ega-octets pour des nuages de 100.000 points), et ne n´ecessite pas de communications intensives et rapides, comme nous le verrons par la suite.

Pour parall´eliser un algorithme, il faut distinguer (voir figure 4.3 86) :

– les parties parall`eles : il s’agit des parties o`u plusieurs calculs ind´ependants doivent ˆ

etre men´es. Dans le cas de l’ICP et ses variantes, il s’agit des traitements pour chaque point de la sc`ene (la recherche des voisins, le calcul des distances, des poids, ...), qui sont ind´ependants pour deux points de la sc`ene diff´erents. C’est bien ´evidement ces calculs que nous devrons r´epartir entre les diff´erentes machines.

– les parties s´equentielles : il s’agit des calculs qui ne peuvent pas ˆetre rendus ind´ e-pendants, et ne peuvent donc ˆetre parall´elis´es. Dans notre cas, ce sera le calcul de la nouvelle estim´ee de la transformation. D’une part, ce calcul d´epend des r´esultats des calculs pour chaque point de la sc`ene, et on doit donc attendre que tous les points de la sc`ene aient ´et´e trait´es avant de pouvoir l’effectuer. D’autre part, il ne peut lui-mˆeme pas se d´ecomposer en calculs ind´ependants, et ne peut donc ˆetre effectu´e que sur une seule machine.

Le gain de temps entre la version classique et la version parall`ele de l’algorithme est conditionn´e par les temps relatifs des calculs parall`eles et s´equentiels. En notant ts le

Fig. 4.3 – Division des calculs en phases parall`eles et s´equentielles.

temps total des calculs s´equentiels (identique dans les deux versions), tp le temps des calculs parall`eles dans la version classique et tpi les temps des calculs parall`eles effectu´es sur la machine i dans la version parall´elis´ee, on peut exprimer le temps de la version classique Tclassique= ts+ tp et le temps de la version parall`ele Tparallele = ts+ max(tpi). Le temps optimum sera atteint quand d’une part la parall´elisation en elle-mˆeme n’introduit pas de temps suppl´ementaire (dˆu par exemple `a la communication entre les machines ou `a l’algorithme de r´epartition des calculs entre les machines), i.e. quandP tpi = tp, et quand, d’autre part les calculs sont ´equitablement r´epartis entre les machines, i.e. quand les tpi

sont tous ´egaux. On a alors tpi = tp/n o`u n est le nombre de machines. Le rapport optimum entre les temps de calcul des deux versions est donc : Tparallele

Tclassique = max(tpi)+ts

tp+ts = tp/n+ts

tp+ts que l’on peut ´ecrire plus simplement :

Tparallele Tclassique = 1 − F n + F avec F = ts tp + ts

Cette loi est appel´ee loi d’Amdahl et donne donc le facteur d’acc´el´eration maximum possible, en fonction du pourcentage de calcul s´equentiel.

Dans le cas de l’ICP, ce pourcentage est tr`es faible (le temps de calcul de la nouvelle transformation est n´egligeable), si bien que le temps de calcul est th´eoriquement divis´e par un facteur proche du nombre de machines mises en jeux, `a condition de bien r´epartir les calculs et ne pas perdre de temps avec les instructions de la parall´elisation elles-mˆemes.

4.6.2 Parall´elisation statique

Nous allons dans un premier temps montrer comment r´epartir les calculs entre les diff´erentes machines sans chercher `a rendre cette r´epartition efficace. Pour cela nous allons diviser la sc`ene en plusieurs sous-nuages qui seront trait´es sur chaque machine. Nous verrons comment regrouper ensuite les r´esultats des traitements. Nous utiliserons pour

cela une biblioth`eque appel´ee MPI, que nous allons maintenant pr´esenter.

MPI

Nous devons faire communiquer les machines entre elles. Nous utiliserons pour cela le protocole MPI, qui pr´esente l’avantage d’ˆetre standardis´e et disponible sur la plupart des machines et syst`emes d’exploitation possible (et en particulier sur les PC Linux utilis´es chez Epidaure et sur les PC Windows utilis´es chez AREALL). Les diff´erentes versions de MPI se composent de :

– une biblioth`eque de fonctions d´edi´ees `a la communication entre machines que l’on peut directement utiliser en C et C++.

– un moteur (un ex´ecutable) permettant de lancer simultan´ement sur plusieurs ma-chines plusieurs instances d’un mˆeme programme (ou mˆeme plusieurs programmes diff´erents) ainsi que les modules qui leur permettront effectivement de communiquer grˆace aux fonctions de la biblioth`eque.

Lorsqu’on lance les diff´erentes instances `a l’aide du moteur, celui-ci va leur attribuer des num´eros entre 0 et n − 1 (o`u n est le nombre d’instances). On pourra en particulier faire jouer un rˆole particulier `a l’une de ces instances (la 0 en g´en´eral), que l’on appellera alors le maˆıtre, les autres ´etant les esclaves.

Nous aurons besoin des fonctions MPI suivantes :

– MPIInit(), MPICommrank(int& id proc), MPICommsize(int& nb proc) : ces fonc-tions permettent d’initialiser le processus de communication en d´ebut de programme et de connaˆıtre le num´ero d’identification de l’instance ainsi que le nombre total d’instances.

– MPIReduce(variables, int id maitre) : cette fonction additionne les variables des es-claves aux variables du maˆıtre, identifi´e par son num´ero d’instance (souvent 0). – MPIBroadcast(variables, int id maitre) : cette fonction copie les variables du maˆıtre

dans celles des esclaves.

Ces fonctions sont bloquantes : il faut attendre que tous les processeurs aient appel´e la mˆeme fonction avant qu’elle ne lib`ere la main et laisse ainsi chaque instance poursuivre ses calculs. Il sera donc pr´ef´erable de ne les utiliser qu’en d´ebut et fin de phase parall`ele pour synchroniser les diff´erents processus `a ces moments l`a, et de ne pas les appeler pendant la phase parall`ele elle-mˆeme pour ne pas bloquer les calculs.

Parall´elisation formelle de l’ICP/EM

Rappelons maintenant l’algorithme de l’ICP/EM :

Initialisation : Calculer une premi`ere estim´ee de T , et mettre le param`etre d’´echelle σ2

`

R´ep´eter

// D´ecimation : D´ecimer la sc`ene avec un rayon de sph`ere α.σ. ´

Etape E : Pour chaque point si ide la sc`ene d´ecim´ee avec le poids de d´ecimation nsi:

Chercher les points du mod`ele mj tels que kT ? si− mjk2 < σ22

max en utilisant un kD-Tree.

Calculer les poids (AT)ij suivant l’eq. 4.12 ´

Etape M : R´e-estimer la transformation T en minimisant P

ijnsi(AT)ij.kT ? si− mjk2.

Recuit simul´e : Diviser σ2 par le coefficient de recuit. Si σ2 est inf´erieur `a l’´echelle finale, le mettre `a l’´echelle finale.

Jusqu’`a convergence

Nous devons maintenant nous demander quelle est la plus grande partie que nous pouvons parall´eliser. On ne peut ´evidemment pas effectuer plusieurs it´erations simultan´ement car une it´eration d´epend de la pr´ec´edente.

A l’int´erieur d’une it´eration, les calculs pour chaque point de la sc`ene sont ind´ epen-dants. On peut donc r´epartir les points entre les diff´erents processeurs et ne r´ecup´erer que leur r´esultat sur un maˆıtre. Mais jusqu’o`u peut-on synth´etiser le r´esultat sur un processeur donn´e pour limiter au maximum les communications et les d´es´equilibres entre maˆıtre et esclaves? Les grandeurs qui nous int´eressent sont les suivantes :

– Le crit`ere C(T ) =P

ilogP

jp(si/T ? mj) et ses variantes (e.g. la distance quadra-tique moyenne entre points appari´es).

– La nouvelle transformation, dont le calcul par la m´ethode de la SVD (voir 3.2.4) est bas´e sur les barycentres des nuages pond´er´es comme s =

P i(P jAijsi) P i(P jAij) pour la sc`ene et sur la matrice de corr´elation en rep`ere barycentrique K =

P i(P jAij.mj.st i) P i(P jAij) − m.st. – Les statistiques comme la distance quadratique moyenne

P i(P jAijd2(si,mj)) P i(P jAij) .

On peut constater que toutes ces grandeurs peuvent ˆetre d´efinies comme des accumulations de grandeurs pour chaque point de la sc`ene, i.e. mises sous la forme F = P

i f (si) : on peut donc se contenter de transmettre les valeurs par point de la sc`ene f (si) voire les accumuler sur chaque machine et ne transmettre que les valeurs pour des groupes de points P

{i1,...,ip} f (si).

Finalement nous pouvons sur un processeur effectuer les calculs pour un groupe de points donn´e, accumuler les valeurs f (si), et attendre que tous les points aient ´et´e trait´es pour effectuer les communications (l’addition finale sur le maˆıtre des sommes partielles de chaque instance, `a l’aide de la fonction MPIReduce) et les calculs s´equentiels sur

le maˆıtre (le calcul de la nouvelle transformation, les affichages, le passage `a l’it´eration suivante). Il faudra ajouter `a cela des communications au d´ebut de la nouvelle it´eration (la communication de la nouvelle transformation aux esclaves, `a l’aide de la fonction MPIBroadcast). Ainsi le temps de communication et le temps s´equentiel est extrˆemement r´eduit.

En ce qui concerne la r´epartition des points, nous nous contenterons ici d’une r´ e-partition ´equitable en nombre entre les diff´erentes instances du programme. L’instance de num´ero id devra donc traiter les points int((id ∗ nbpoints)/nbprocess) `a int(((id + 1) ∗ nbpoints)/nbprocess) − 1 o`u int d´esigne la partie enti`ere. C’est tellement simple qu’aucune communication ne sera n´ecessaire pour faire connaˆıtre cette r´epartition.

Lors de l’impl´ementation effective de l’algorithme, il faudra bien v´erifier que la ver-sion parall`ele produit bien exactement les mˆemes r´esultats que la version classique. Nous l’avons bien ´evidement syst´ematiquement v´erifi´e, et n’aborderons plus ce point par la suite.

4.6.3 Parall´elisation dynamique

La parall´elisation statique permet d’atteindre des vitesses satisfaisantes (sup´erieures `

a la moiti´e de la vitesse maximale th´eorique). Les temps pour chaque point de la sc`ene peuvent varier ´enorm´ement (en fonction du nombre de points du mod`ele `a proximit´e), et la r´epartition ´equitable en nombre de points `a traiter n’est donc pas synonyme de r´epartition ´equitable en temps. Mais ces d´es´equilibres sont att´enu´es par le grand nombre de points si bien que les temps de calculs de chaque instance restent voisins. Cependant ces r´esultats se d´egradent lorsqu’apparaissent des d´es´equilibres entre la puissance des diff´erentes machines, soit parce qu’elles sont diff´erentes soit parce que certaines sont d´ej`a occup´ees. La machine la plus lente fait alors attendre toutes les autres pour la phase s´equentielle. Ainsi nous avons compar´e les r´esultats entre une grappe de n bi-pros `a 935 MHz et une grappe de n bi-pros `a 935 MHz et 1 bi-pro `a 500 MHz. Nous avons trac´e le temps de calcul en fonction de la puissance totale disponible (figure 4.4 gauche). L’exp´erience confirme qu’un seul processeur moins rapide retarde significativement les autres. Le rendement (rapport entre temps effectif et temps th´eorique) varie entre 50 et 80 %.

Pour rem´edier `a cet inconv´enient, on utilisera une technique usuelle de r´epartition dynamique des calculs appel´ee vol de tache : un processeur qui a fini ses calculs propose au plus charg´e de lui reprendre une partie de ses calculs pour le soulager. Ceci suppose que les processeurs puissent communiquer entre eux pour se tenir inform´es de l’avancement de leur tache. On peut envisager plusieurs strat´egies :

– Une strat´egie maˆıtre/esclave : un processeur joue ici un rˆole particulier, celui de centraliser les informations. Chaque esclave ne peut communiquer qu’avec le maˆıtre, ce qui simplifie beaucoup le nombre de messages en circulation.

Fig. 4.4 – Temps de calcul de l’ICP en fonction de la puissance disponible : temps th´ eo-rique sur une seule machine, temps mesur´e sur une grappe homog`ene et sur une grappe h´et´erog`ene. Les rendements se situent entre 50 et 80 % pour la parall´elisation statique (`a gauche) et sont sup´erieurs `a 90 % pour la parall´elisation dynamique (`a droite).

– Une strat´egie compl`etement connect´e : aucun processeur ne joue de rˆole particulier, et tous peuvent communiquer avec tous. Ceci permet d’acc´el´erer les communications (on ne passe par aucun interm´ediaire), mais il peut y avoir plusieurs messages en circulation au mˆeme moment, ce qui peut devenir complexe `a g´erer.

– Une strat´egie cyclique : chaque processeur ne peut communiquer qu’avec le suivant (et le dernier avec le premier), formant un cycle. Ainsi, tous jouent le mˆeme rˆole et il devient tr`es simple de contrˆoler le nombre de messages en circulation. En revanche, la communication entre deux processeurs ´eloign´es sur le cycle passera par plusieurs interm´ediaires et sera donc plus lente (de l’ordre de 1 ms pour 10 processeurs). Nous choisirons une strat´egie cyclique, pr´ef´erant la simplicit´e `a la rapidit´e. Nous simplifie-rons encore en faisant circuler en permanence un unique message sur le cycle. Pour cela, nous utiliserons les fonctions de communication non-bloquantes entre deux machines de MPI :

– ISend(destinataire, message, id message) : demande au moteur MPI d’envoyer un message `a un destinataire donn´e. L’envoi n’est pas forc´ement effectu´e imm´ ediate-ment, mais la main est rendue au programme, permettant ainsi de poursuivre les calculs. Un num´ero d’identification est donc donn´e au message, permettant de tester ult´erieurement s’il a bien ´et´e envoy´e.

– IRecv(exp´editeur, message, id message) : demande au moteur MPI de se pr´eparer `a recevoir un message d’un exp´editeur donn´e. La main est rendue imm´ediatement au programme, permettant ainsi de poursuivre les calculs. Un num´ero d’identification est donc donn´e au message, permettant de tester ult´erieurement s’il a ´et´e re¸cu. – Test(id message) : permet de tester si le message a ´et´e effectivement envoy´e (suite `a

– Wait(id message) : attend que le message ait ´et´e effectivement envoy´e (suite `a ISend) ou re¸cu (suite `a IRecv). Cette instruction est donc bloquante.

Ainsi, nous ne perdrons pas de temps avec la communication. L’inconv´enient est que nous ne serons pas imm´ediatement pr´evenu qu’un message est arriv´e, il faudra attendre pour cela l’appel effectif de la fonction Test. En pratique, nous appellerons la fonction Test apr`es le calcul de chaque point de la sc`ene, car la fr´equence de traitement est inf´erieure `a celle des communications (0.1ms par point).

Le message que nous ferons circuler contiendra les informations suivantes :

– Le nombre de points d´ej`a trait´es par chaque processeur : ceci permettra d’avoir une id´ee de la vitesse de chacun pour pouvoir optimiser la r´epartition.

– Le nombre de points restant `a traiter : ceci permettra de savoir quels processeurs sont disponibles (ceux qui n’ont plus de points `a traiter) et lesquels sont encore tr`es charg´es.

– Une instruction ´eventuelle, permettant d’indiquer les op´erations `a faire pour r´ealiser effectivement le vol de tˆache : il y a deux instructions possibles : la proposition de vol (l’instance i propose de soulager l’instance j), l’acceptation de vol (l’instance i confie les points n1 `a n2 `a l’instance j).

L’algorithme de r´epartition dynamique doit tenir compte de l’aspect p´erim´e des messages : le temps que le message transite entre l’instance i et j, l’instance i aura trait´e de nouveaux points. Les informations la concernant ne seront pas `a jour. L’instance j, si elle est libre, ne peut donc pas se lancer aussitˆot dans le traitement de points d’une autre instance, car celle-ci les a peut-ˆetre d´ej`a trait´es entre temps. Elle doit donc se contenter de proposer ses services et attendre qu’on lui r´eponde.

La question est alors de savoir `a qui elle offre ses services. Elle peut le d´ecider elle-mˆeme en jugeant la vitesse et la charge de chaque processeur, informations h´elas p´erim´ees. On pourrait alors penser laisser ce choix aux autres processeurs, au moment o`u ils re¸coivent le message : le premier processeur accepte le vol de tache, puis le deuxi`eme lui reprend s’il est plus charg´e (il juge alors sur des informations r´ecentes) et ainsi de suite. Mais il faudrait alors signaler au premier processeur que ce n’est finalement pas lui qui a ´et´e d´elest´e, ce qui complique la communication.

Nous avons finalement opt´e pour la solution suivante : l’instance libre choisit l’instance qu’elle va d´elester et envoie le message ad´equat, et l’instance `a d´elester choisit lorsqu’elle le re¸coit le nombre de points optimal `a donner, et envoie le message ad´equat. Finalement, le vol de tache se fait en un seul cycle de communication.

Le choix du nombre de points `a voler se fait de la fa¸con suivante : si vi est le nombre de points d´ej`a trait´e et ci le nombre de points restant pour l’instance i (cj = 0 pour l’instance libre), la nouvelle r´epartition des points c0i doit ˆetre proportionnel au nombre de points

d´ej`a trait´e :

c0i = vi P vi

X ci

Si le temps de traitement des points ´etait uniforme, cette r´epartition serait optimale (le temps de calcul restant, proportionnel `a ci/vi, est identique pour toutes les instances). Enfin, pour le choix de la meilleure instance `a d´elester, on calcule c0i pour chaque instance encore charg´ee, et on choisit celle pour laquelle le gain de temps (ci − c0

i)/vi est le plus important.

L’algorithme final au niveau de chaque instance est finalement le suivant :

Si le message a ´et´e re¸cu par l’instance j

Mise `a jour : On met `a jour les informations relative `a l’instance j (nombre de points d´ej`a trait´es et restant `a traiter)

Confirmation : Si une instruction du type acceptation de vol concernant l’instance j est lanc´ee (l’instance i confie les points n1 `a n2 `a l’instance j)

On lance le traitement des points n1 `a n2 sur l’instance j, et on met `a jour le message (nouveau nombre de points `a traiter pour j, effacement de l’instruction de vol)

Acceptation : Si une instruction du type proposition de vol concernant l’instance