350 likes | 497 Views
Algorithmique parallèle. Construction et analyse d’algorithmes B. Le Cun Synthèse par transformation S. Rajopadhye Synthèse par interprétation J.-L. Roch. h. Un exemple. void explore( nœud n, …) { if … { for (s =n.first(); s <n.last(); n++) { dopar explore( s, …) ; } }.
E N D
Algorithmique parallèle • Construction et analyse d’algorithmes B. Le Cun • Synthèse par transformation S. Rajopadhye • Synthèse par interprétation J.-L. Roch
h Un exemple void explore( nœud n, …) { if … { for (s =n.first(); s <n.last(); n++) { dopar explore( s, …) ; } } tâche T1 : tps séquentiel T= O(h) S1 = O(h) Texec(p) ~ T1 /p (1+e) Sexec(p) = S1O(1)
F(2,a) G(a,b) H(b) b a H(a) O(b,7) Interprétation= Analyse de flot +ordonnancement Sisal, Cilk, Ath1,… De l’algorithme à la machine : Synthèse par interprétation Programme parallèle T1, T : Exécution efficace Class f { operating()( shared_r_w<int> x ) { x = sin(x->val ) * BB(taq ( x ) ) ; } void main(int argc, char* argv) { Fork<f> ( shared<int> (3) ) ; }
Fonctionnel[70, …] Calcul par nécessité Haskell [Arvind 70, …MIT ] Sisal [ 90, Gaudiot USC] … mémoire Parallélisme[80, …] Ordonnancement efficace - fonctionnel Multilisp [Halstead 86, MIT] Prolog [Chassin 88] Mars-ML [Lecussan 88] Sisal [Sohn, 98 NJIT] Nesl [Blelloch 96 Cornell] - impératif : Macro data flow Jade [Rinard 95, Stanford] Earth [Gao 98 UD] Cilk [Leiserson 98, MIT]Athapascan-1 [Roch 98] temps + mémoire Langages data flow détection (dynamique) des dépendances
Plan 1. Ordonnancement d’un graphe: calcul et contrôle • Interprétation data flow : techniques de base 3. Optimisation dynamique du grain 4. Optimisation dynamique des accès mémoire • Contrôle de l’explosion mémoire
F(2,a) G(a,b) H(b) b a H(a) O(b,7) Ordonnancement : cadre théorique • Entrée : un graphe de précédence, pas un programme • Sortie : un placement des tâches et des données • Analyse de l’ordonnancement : comparaison à l’optimal • Coût de calcul et de gestion généralement ignorés : • Coût de calcul polynomial en le nombre de tâches -> T1O(1)
Ordonnancement glouton : minimiser l’inactivité [Graham66] T1 =durée d’une exécution séquentielle T=durée d’un chemin critique + surcoût • Prise en compte des latences liées au réseau : ex : analyse de coûts de communication : ETF / ERT [Hwang89] C1 = volume total de communications C = volume des communications sur un chemin critique • = délai de communication d’une donnée + surcoût Exemples d’ordonnancements
h Surcoût pour réaliser l’ordonnancement • Contrôle : • création/placement/entrelacement des tâches • gestion de la mémoire, mouvements de données • préemptivité, réactivité • Exemple : algorithme récursif d’exploration • Ordonnancement glouton : théorique = OK • Mais réalisation ??? • Nombre de tâches ~ T1 … • Mémoire ~ 2h … en séquentiel ~ h
Contrôle ordonnancement sur grappe • Surcoût • local sur SMP : • détection locale des données disponibles : fins d’accès locaux • gestion locale des tâches prêtes sur threads dédiés • global entre nœuds SMP : • Calcul des synchronisations : bilan des fins d’accès locaux • si inactivité : envoi d’une requête à un autre nœud • Surcoût de réactivité
Plan 1. Ordonnancement d’un graphe: calcul et contrôle • Interprétation data flow : techniques de base distribué, SMP, séquentiel 3. Optimisation dynamique du grain 4. Optimisation dynamique des accès mémoire
Interprétation data-flow (1) • Détecter dynamiquement les synchronisations : dépendance écriture -> lecture • Variables vues en assignation unique [sisal…]: • Une tâche lit des valeurs en mémoire; • L’effet d’une tâche est d’écrire des valeurs en mémoire; • En cours d’exécution d’une tâche, une variable a deux versions : • version courante : ce qui est lu : valeur précédente avant l’instruction courante • version future : ce qui sera écrit : valeur résultant de l’exécution de l’instruction sera vue par les successeurs
Avant dopar f(x) X Ramassage x côté exécution f(x) courante future courante courante X local après dopar f(x) future • En fin d’accès à une variable : release • La version «future / courante » devient prête : • ramassage de l ’ancienne version courante • les successeurs de f(x) deviennent prêts Interprétation data-flow (2) • Lors de la création de tâches : dopar f(x,…) ; • création d’une nouvelle version pour les variables modifiées par la tâche créée courante côté appelant == future côté appelé Fin exécution f(x)
Interprétation data-flow (3) • Calculer si une version est prête : • fin écriture => lecture possible • fin lecture : écriture en place/ramassage mémoire possible • Implémentation de la gestion des versions : • dépend de la sémantique des accès mémoire • Distribuée, SMP, Séquentielle
Implémentation distribuée • Une même version est distribuée sur plusieurs sites: • nommage : site de création + indice sur le site : x = <site, indice> • Calculer si une version est prête : • Terminaison distribuée • Exemple: Version[x] Version[x] bilan + cptr local id site de ref + cptr local Version sur site de référence Version sur autre site
Courante cpt T& val Future cpt Implémentation SMP • Compteur local sur chaque version : • nombre d ’accès en cours : prêt <=> cptr==0
courante x future Implémentation séquentielle (DF) • ordonnancement d’une tâche = appel de fonction dopar f( x ); • version courante == version future : stockage en place implantation f(x) implantation x
Interprétation data flow • Efficace à gros grain en distribué et smp mais … • Surcoût de contrôle dû à l’interprétation • Comment gérer de nombreuses synchronisations ? • Analogie : interprétation vs compilation • Comment compiler (à la volée) l’ordonnancement ? …JIT
Plan 1. Ordonnancement d’un graphe: calcul et contrôle 2. Interprétation data flow : techniques de base 3. Optimisation dynamique du grain dégénération séquentielle 4. Optimisation dynamique des accès mémoire
<i1> f1(args) … fk(args) sync <fin> Tirer parti d’un ordt séquentiel efficace • Programme très parallèle : • beaucoup de tâches créées et exécutées localement [Blumhoffe] • void prog() { <i1> spawn f1 (args ) ; … //autres spawn Sync <fin> } • Mettre le surcoût d’ordt lors du vol : éviter les créations de tâches, remplacées par des appels de fonction
Quand est-ce possible ? • Lors d’une création, il faut pouvoir dégénérer en appel de fonction profondeur d’abord : • Sémantique séquentielle possible • Être sûr que la tâche est prête à être exécutée • Exemples : multilisp [86], Prolog,…, Cilk [98], … • Deux versions d’une fonction : • Séquentielle - Création/exécution d’une tâche
Sinon {création tâche pour f1(args)} f1(args) sync Processeur inactif Ordt interprété <fin> • Création de deux tâches : exportation + synchronisation Dégénération séquentielle « active » • Principe : test si processeur inactif avant de dégénérer <i1> Si pas de requête de tâches { dégénération séquentielle f(args1) ;… } f1(args) … fk(args) Processeur actif Ordt compilé
<i2…> Si (continuation volée){ signaler sync; return; } Sinon suite idem… … fk(args) sync <fin> Processeur inactif Ordt interprété Dégénération séquentielle « passive » // dégénération séquentielle <i1> ; Préparation continuation exportable; f(args1) ; // version rapide <i1> • Principe : si requête de vol, vol de la continuation f1(args) … fk(args) sync <fin> Processeur actif Ordt compilé
Sans dégénération séq [Ath-1] Avec dégénération séq [Cilk] Environ 220 tâches de grain 1 Cilk Ath-1 Dégénération séquentielle - Conclusion • Théorique sur SMP : si programme parallèle strict • Pratique : contrôle automatique de granularité [Galilée98] + O(p.T) SMP : durée = f( seuil ) temps séquentiel pour 1 processeur pour 2 processeurs
Plan 1. Ordonnancement d’un graphe: calcul et contrôle 2. Interprétation data flow : techniques de base 3. Optimisation dynamique du grain 4. Optimisation dynamique des accès mémoire dégénération distribuée
Optimiser les accès mémoire • Si beaucoup de synchronisations : • efficacité si localité • Analyse data flow => Les accès distants sont prévisibles • Pré-calcul de sites pour tâches et données (écriture locale) [ETF] • Accès mémoire « compilé » == pointeur local ou communication « uni-directionnelle » Dégénération distribuée • Surcoût mis lors de non respect de la localité
Exemple • Pré-affectation tâches/versions aux processeurs Accèsmémoire F(a) G(b) comm b a send a H(a) recv I(a,b) Processeur 1 Processeur 2
Algorithme d’interprétation • Hypothèse : graphe calculé dynamiquement (partiellement) • Version = ptr+ressource de communication (ex: tag mpi) • En fin d’écriture, diffusion à tous les sites lecteurs • Regroupement des communications / contrôle scrutation • Côté lecteur : • pré-allocations pour les réceptions • Ordonnancement dynamique des tâches prêtes • NB: le pré-placement peut être modifié dynamiquement
+ O(p.n2 + t.n) Dégénération distribuée ETF Dégénération distribuée bloc-bidim DTPORF 5000 tâches 16 procs (IBM-SP1) ScaLapack/MPI bloc-bidim 16 proc MFlops = f( taille ) Bloc bidimsans dégénération glouton Dégénération distribuée - Conclusion [Doreille99] • Théorique : sur modèle délai : latence = t
Plan 1. Ordonnancement d’un graphe: calcul et contrôle 2. Interprétation data flow : techniques de base 3. Optimisation dynamique du grain 4. Optimisation dynamique des accès mémoire
Conclusion (1) • Langages « macro data flow »: • JIT Analyse (implicite) des dépendances • Calcul d’ordonnancement • Contrôle de l’ordonnancement : • Techniques pour optimiser le surcoût de contrôle Dégénération séquentielle Dégénération distribuée • Intégration dans d’autres langages • Dégénération séquentielle dans Open-MP ? • Spécialisation automatique : • Les techniques peuvent être couplées entre elles • Vers un ordt générique avec auto-spécialisation
dtrporf : grappe de 64 processeurs [Athapascan-1 – 60000 tâches] • 16 quadri-processeurs [Univ. Delaware] • Grappe SUNs[Solaris] + Myrinet • bande passante 30Mo/s , latence 60µs • 1560 Mflops par nœuds= 25000 Mflops. Dégénération distribuée entre nœuds Ordt glouton optimisé SMP sur chaque noeud [Bloc bidim/MPI] • Exploitation mémoire partagée • Ordonnancement « plus dynamique »
Conclusion (2) • Construction d’un algorithme parallèle efficace : • À la main : algorithmique parallèle • Automatiquement par transformation d’un programme séquentiel • Exécution d’un programme parallèle • Automatiquement par interprétation d’un programme parallèle • Intérêt d’un ordonnancement séquentiel efficace + Prédiction dépendances • À la main : écriture d’un programme spécifique • La synthèse automatique permet, sous certaines restrictions, la portabilité avec performances
Programme Fichieren entrée Partition dynamique en blocs Partition dynamique en blocs Compressionà la volée Compressionparallèle Blocs compressés Fichiercompressé Si processeur inactif : nouveau bloc Comment paralléliser gzip ? Parallélisation