• Aucun résultat trouvé

4.4 Compilation de BSML

5.1.4 Fonctions pour le calcul des préfixes

Notre troisième exemple est le calcul parallèle classique des préfixes d’un ensemble de valeurs avec un opérateur associatif admettant un élément neutre. Pour représenter cet ensemble, nous faisons l’hypothèse que chaque processeur en mémorise une partie sous la forme d’une liste d’éléments. Nous avons donc un vecteur parallèle de listes d’éléments. Prenons pour exemple, l’expression suivante pour une machine à 3 processeurs (avece = 1 comme élément neutre) permettant de calculer les factorielles de 1 à 5 :

scan_list e (×) [1; 2] [3; 4] [5]

Cette expression sera évaluée en :

[e × 1; e × 1 × 2] [e × 1 × 2 × 3; e × 1 × 2 × 3 × 4] [e × 1 × 2 × 3 × 4 × 5]

⇒ [1; 2] [6; 24] [120]

Chaque processeur effectue une réduction locale avec les éléments de sa propre liste, puis envoie son résultat partiel aux processeurs qui le suivent (dans l’ordre des pid de la machine) et finalement réduit ce résultat avec les valeurs envoyées par les processeurs qui le précèdent.

Pour ce calcul, nous avons donc besoin, tout d’abord, d’une fonction pour la réduction parallèle, c’est-à-dire réduire, via un opérateur, les valeurs d’un vecteur parallèle. Ceci équivaut à calculer au processeur

i, la réduction des valeurs contenue par les processeurs j tel que j < i. Prenons par exemple, l’expression

suivante :

scan e (×) 1 2 3

Celle-ci sera évaluée en :

e 1 × 2 1 × 2 × 3

⇒ 1 2 6

La figure 5.6 donne le code BSML pour une telle fonction en utilisant l’algorithme BSP direct [34]. Nous employons les fonctions suivantes :

       List.fold_left: (α →β →α)→α →βlist→α List.fold_leftf e [v0; . . . ; vn] = f (· · · (f (f e v0) v1) · · · ) vn from_to:int→int→int list

from_ton m = [n; n + 1; n + 2; . . . ; m].

Ensuite, nous pouvons obtenir une version générique pour le calcul des préfixes en utilisant les fonctions suivantes :

(scan_wide :((α →β →β)→γ → δpar→αpar)((α →β →β)→γ → ǫ → δ ∗ ζ)

((β →β)→ ζ → η )(α →β →β)→γ → ǫpar→ ηpar)

let scan_wide scan seq_scan_last map op e vl = let local_scan=parfun (seq_scan_last op e) vl in let last_elements=parfun fst local_scan in let values_to_add=(scan op e last_elements) in

0 0.02 0.04 0.06 0.08 0.1 0.12 0.14 0.16 0.18 0.2 10000 20000 30000 40000 50000 60000 70000 80000 90000 100000 Temps (s)

Nombres d’elements de la liste Eparpillement d’une liste d’entiers avec MPI avec PUB avec TCP 0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2 100000 200000 300000 400000 500000 600000 700000 800000 900000 1e+06 Temps (s)

Nombres d’elements de la liste Eparpillement d’une liste d’entiers avec MPI

avec PUB avec TCP

73 5.1. FONCTIONS BSP CLASSIQUES AVEC LA BSMLLIB 0 0.05 0.1 0.15 0.2 0.25 0.3 0.35 10000 20000 30000 40000 50000 60000 70000 80000 90000 100000 Temps (s)

Nombres d’elements de la liste Diffusion en 2 phases d’une liste d’entiers avec MPI avec PUB avec TCP 0 0.5 1 1.5 2 2.5 3 3.5 4 100000 200000 300000 400000 500000 600000 700000 800000 900000 1e+06 Temps (s)

Nombres d’elements de la liste Diffusion en 2 phases d’une liste d’entiers avec MPI

avec PUB avec TCP

(scan_direct: (α →β →α)→α →βpar→αpar)

let scan_direct op e vv =

let mkmsg pid v dst=if dst<pid then None else Some v in let procs_lists=mkpar (fun pidfrom_to 0 pid) in

let rcv_msgs=put (apply (mkpar mkmsg) vv) in

let values_lists= parfun2 List.map (parfun (compose noSome) rcv_msgs) procs_lists in

applyat 0 (fun _→e) (List.fold_left op e) values_lists

Figure 5.6 —Code de l’algorithme direct pour la réduction parallèle

let pop=applyat 0 (fun _ yy) op in

parfun2 map (pop values_to_add) (parfun snd local_scan)

(seq_scan_last: (α →β →α)→α →βlist→α ∗αlist)

let seq_scan_last op e l = let rec seq_scan’ last l accu =

match l with

[]→(last,[last])

| [hd]→(last,(op last hd)::accu) | hd::tl→(let new_last = (op last hd)

in seq_scan’ new_last tl (new_last::accu)) in

seq_scan’ e l []

(scan_list : ((α →α →α)→α →αpar→αpar)(α →α →α)→α →αlist par→αlist par)

let scan_list scan op e vl = scan_wide scan seq_scan_last List.rev_map op e vl

tel queseq_scan_lastf e [v0; v1; . . . ; vn] = (last, [(f last vn); · · · ; (f (f e v0) v1); (f e v0)]) avec last = (f (· · · (f (f e v0) v1) · · · ) vn−1). Nous pouvons alors composer les fonctions définies ci-dessus pour

obtenir notre fonction :

(scan_list_direct:(α →α →α)→α →αlist par→αlist par)

let scan_list_direct op e vl = scan_list scan_direct op e vl

La formule de coût BSP d’une telle fonction (en faisant l’hypothèse queopa un coût constantcop) est :

2 × N × cop× r + (p − 1) × s × g + l

telle ques est la taille en octets d’un élément et N est la longueur de la plus longue liste contenue par un

processeur. Nous avons donc la somme des coûts pour calculer la réduction partielle, échanger ces résultats et finir la réduction avec les éléments échangés.

La figure 5.7 montre les performances mesurées avec en chaque processeur une liste de flottants (chacune de même longueur). La somme de flottants a été utilisée comme opérateur. Sur les deux graphiques, les longueurs de ces listes vont croissant.

Il est aussi possible de calculer les préfixes enlog2(p) + 1 super-étapes [34]. La figure 5.8 donne le code

BSML pour une telle fonction. Nous pouvons alors recomposer les fonctions définies ci-dessus pour avoir une nouvelle version du calcul des préfixes :

(scan_list_direct:(α →α →α)→α →αlist par→αlist par)

let scan_list_logp op e vl = scan_list scan_logp op e vl

La formule de coût BSP d’une telle fonction (en faisant l’hypothèse queopa un coût constantcop) est :

2 × N × cop× r + (log2(p) + 1) × (g + l)

avecs qui est la taille en octets d’un élément et N qui est la longueur de la plus longue liste contenue par un

processeur. Nous avons donc la somme des coûts pour calculer la réduction partielle, échanger ces résultats enlog2(p) + 1 super-étapes et finir la réduction avec les éléments échangés.

La figure 5.9 montre les performances mesurées avec en chaque processeur une liste de flottants (chacune de même longueur). La somme de flottants a été utilisée comme opérateur. Sur les deux graphiques, les longueurs de ces listes vont croissant.

75 5.1. FONCTIONS BSP CLASSIQUES AVEC LA BSMLLIB 0 0.002 0.004 0.006 0.008 0.01 0.012 0.014 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 Temps (s)

Nombres d’elements en chaque processeur Calcul direct des prefixes (somme) de listes de flottants avec MPI avec PUB avec TCP 0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2 100000 200000 300000 400000 500000 600000 700000 800000 900000 1e+06 Temps (s)

Nombres d’elements en chaque processeur Calcul direct des prefixes (somme) de listes de flottants avec MPI

avec PUB avec TCP

(scanlogp: (α →α →α)→α →αpar→αpar)

let scan_logp op e vec = let rec scan_aux n vec =

if n >= (bsp_p()) then (applyat 0 (fun _e) (fun xx) vec) else

let msg = mkpar (fun pid v dst

if ((dst=pid+n)or(pid mod (2∗n)=0))&&(within_bounds (dst−n))

then Some v else None)

and senders = mkpar (fun pid→natmod (pid−n) (bsp_p()))

and op’ = fun x ymatch y with Some y’→op y’ x | None→x in

let vec’ = apply (put (apply msg vec)) senders in let vec’’= parfun2 op’ vec vec’ in

scan_aux (n∗2) vec’’ in scan_aux 1 vec

Figure 5.8 —Code de l’algorithme binaire pour la réduction parallèle

Documents relatifs