norme: array float -> float
plus_proche_voisin: ~distance:('a -> 'a -> float) -> 'a -> 'a array -> 'a
filter: ('a -> bool) -> 'a array -> 'a array
ecart_type: array float -> float
Domainslib
, définissez une fonction ultra_parallel_map_reduce: Task.pool -> ('a -> 'b) -> ('b -> 'b -> 'b) -> 'b -> 'a array -> 'b
qui crée une tâche par case du tableau. Vous pouvez vous inspirer du comptage de nombres premiers vu en cours.chunk_parallel_map_reduce: Task.pool -> ~nb_tasks:int -> ('a -> 'b) -> ('b -> 'b -> 'b) -> 'b -> 'a array -> 'b
qui crée nb_tasks
tâches d'efforts équivalents.On considère l'implémentation suivante de map-reduce, légèrement différente de celle vue en cours.
let parallel_map_reduce f (++) _0 array =
let n = Array.length array in
let acc = ref _0 in
let map_reduce_on_slice from upto =
for i = from to upto - 1 do acc := !acc ++ f array.(i) done in
let d1 = Domain.spawn (fun () -> map_reduce_on_slice 0 (n/2)) in
let d2 = Domain.spawn (fun () -> map_reduce_on_slice (n/2) n) in
Domain.join d1 ; Domain.join d2 ; !acc
Quel est le problème de cette implémentation? Comment le corriger?
On reprend l'exemple vu en cours:
let c = ref 0
let n = 1_000_000
let f () =
let i = ref 0 in
while !i < n do
c := !c + 1;
i := !i + 1;
done
let () = (execute_en_parallele f f ); Format.printf "!c = %d\n" !c
On a vu que la valeur affichée pouvait être plus petite que 2n. Mais quelle est la plus petite valeur qui peut être affichée? (On suppose que chaque incrément est une lecture atomique suivi d'une écriture atomique).
use(mut,mut')
pour lock(mut);lock(mut');unlock(mut);unlock(mut')
Quels programmes peuvent conduire à des deadlocks?use(m1,m2) || use(m2,m3) || use(m3,m1)
use(m1,m2) || use(m2,m3) || use(m1,m3)
lock(m1);use(m2,m3);unlock(m1) || lock(m1);use(m3,m2);unlock(m1)
L'algorithme de Peterson est un algorithme permettant d'implémenter un verrou partagé entre deux threads.
(* à compiler avec ocamlopt *)
let last_interested = Atomic.make 0
let interested = [|Atomic.make false; Atomic.make false|]
let lock me =
Atomic.set interested.(me) true;
Atomic.set last_interested me;
let other = 1 - me in
while (Atomic.get last_interested = me) && Atomic.get interested.(other)
do () done
let unlock me =
Atomic.set interested.(me) false
let n = 10_000_000
let test me c =
for i=1 to n do
lock me;
c := !c + 1;
unlock me
done
let () =
let c = ref 0 in
let d0 = Domain.spawn (fun () -> test 0 c) in
let d1 = Domain.spawn (fun () -> test 1 c) in
Domain.join d0; Domain.join d1;
Printf.printf "!c = %d\n" !c
Atomic.set
dans lock
l'algorithme n'implémente plus l'exclusion mutuelle.Fable : on peut donner une version "30 millions d'amis" de l'algorithme de Peterson. Alice et Bob sont voisins et partagent leur jardin, l'un a un chien, l'autre un chat. Pour sortir leur ami sans risque, ils mettent en place un protocole. Chacun a un drapeau qu'il peut hisser ou abaisser depuis sa maison. Il y a une longue corde qui relie les deux maisons avec un trait au milieu. Pour sortir, il faut hisser son drapeau puis tirer sur la corde. On sort lorsque le drapeau de l'autre n'est pas levé ou le trait sur la corde est chez le voisin. La corde est "atomique" (elle ne casse pas, le trait ne peut pas se retrouver au milieu, etc). Le drapeau est lui aussi atomique (pas de brouillard, la vitesse de la lumière est infinie, etc). Si on veut faire un parallèle avec les modèles mémoires faibles, on peut rajouter que Alice et Bob font les choses exactement dans l'ordre, ils n'ont pas de serviteur à qui ils délèguent et qui va optimiser et changer l'ordre des deux actions, parce que, "de son point de vue", c'est la même chose