Modéliser Parallèle avec C# 4.0<br />Code Session : TCP301<br />21 avril 2010<br />Bruno BOUCARD<br />boucard.bruno@free.f...
Choisir une stratégie et un algorithme adaptés<br />Trouver la concurrence<br />Méthode itérative<br />
Etape 1<br />Mesurer les performances de la solution<br />
Etape 1Analyser les performances<br />Analyser les coûts de la solution séquentielle<br />
Analyser les performances<br />Démonstration<br />
Etape 2<br />Trouver la concurrence<br />
Etape 2Trouver la concurrence<br />Commencer avec un cahier des charges qui décrit le problème<br />Quel que soit le conte...
Il arrive qu’on passe d’une décomposition orientée tâches à une décomposition orientée données ou bien flux de données en ...
 NDepend: http://www.ndepend.com/</li></li></ul><li>Trouver la concurrence<br />Démonstration<br />
Trouver la concurrenceSolution séquentielle<br />Dépiler un nom de fichier<br />WM_TIMER<br />Sélectionner un répertoire<b...
Etape 2Décomposition et granularité<br />Votre décomposition en tâches doit tenir compte de leur granularité et de leur su...
Etape 2Décomposition et répartition de charge<br />Votre groupement de tâches doit tenir compte de leur charge<br />Cœur 1...
Etape 2Evaluer votre design<br />Flexibilité<br />Préférer l’abstraction pour faciliter l’adaptation à différents scénario...
Etape 3<br />Choisir un algorithme en fonction de votre stratégie<br />
Etape 3Choisir un algorithme en fonction de votre stratégie<br />
Etape 3Algorithmes données<br />Décomposition géométrique<br />Taille des morceaux <br /><ul><li> Trop grand – sous utilis...
 Trop petit – sur consommation </li></ul>Format des morceaux<br /><ul><li> Attention au false sharing </li></ul>Traitement...
ModéliserAlgorithmes données – Attention au False Sharing<br />Pour des raisons de performances, les systèmes utilisent de...
Deux champs d'instance dans la même instance de classe sont proches dans leurs emplacement de mémoire
Deux champs statiques dans le même type sont proches en mémoire
Deux éléments avec des index adjacents dans un tableau sont proches en mémoire
Les objets alloués consécutivement sont probablement proches en mémoire
Les variables locales utilisées ensemble dans une fermeture sont probablement capturées dans les champs d'instance, et ain...
Algorithmes données: False Sharing<br />Démonstration<br />
Etape 3Décomposition géométrique<br />for (int i = 0; i < size; i++) {<br />Parallel.For(0, size, j => {<br />inttemp = 0;...
Etape 3Algorithmes données<br />Décomposition récursive<br />Le choix de la profondeur détermine la performance<br />- Arb...
Etape 3Récursivité sur des données<br />staticvoidWalk<T>(Tree<T> root, Action<T> Action)<br />{<br />    if (root == null...
Etape 3Algorithmes tâches<br />Parallélisme de tâches linéaires<br />Nombre de tâches<br /><ul><li> trop peu: les cœurs so...
 trop élevé: contention des tâches</li></ul>Dépendances<br /><ul><li> retirables
 séparables
 lecture seule ou lecture/écriture</li></ul>sous opération 1<br />sous opération 2<br />sous opération 3<br />sous opérati...
Etape 3Algorithmes tâches<br />Arbres profond <br /><ul><li> contention processeurs</li></ul>Arbres de profondeur limitée<...
Etape 3Diviser pour régner<br />staticvoidQuickSort<T>(T[] data, intfromInclusive, inttoExclusive) where T : IComparable<T...
Etape 3Algorithmes flux de données<br />Pipeline<br />Les charges de travail des étapes<br /><ul><li> égales – pipeline li...
 inégales – pipeline non-linéaire</li></ul>Chaîne de montage automobile<br />
Etape 3Algorithmes flux de données<br />Coordination orientée événements<br />Traitement d’une dépêche sur un desk journal...
Etape 3Pipeline<br />            var input = new BlockingCollection<string>();<br />            var readLines = Task.Facto...
Etape 4<br />Choisir un pattern de structure<br />
Etape 4Choisir un pattern de structures de programme <br />Après avoir sélectionné votre algorithme parallèle, il faut mai...
Etape 4Les Patterns de structures de programme<br />SPMD, Master/Worker, Boucle parallèle et Fork/Join partagent les mêmes...
Prochain SlideShare
Chargement dans…5
×

ALT.NET Modéliser Parallèle avec C# 4.0

1 559 vues

Publié le

Dans cette session présentée le 21 avril 2010 à Paris dans les locaux d'OCTO Technology. Vous découvrirez les règles essentielles et les principaux patterns parallèles qui vous aideront à écrire en C# 4.0 des applications parallèles plus évolutives et plus faciles à maintenir pour retrouver le Free Lunch

Publié dans : Technologie
0 commentaire
0 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Aucun téléchargement
Vues
Nombre de vues
1 559
Sur SlideShare
0
Issues des intégrations
0
Intégrations
3
Actions
Partages
0
Téléchargements
22
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

ALT.NET Modéliser Parallèle avec C# 4.0

  1. 1. Modéliser Parallèle avec C# 4.0<br />Code Session : TCP301<br />21 avril 2010<br />Bruno BOUCARD<br />boucard.bruno@free.fr<br />http://blogs.msdn.com/devpara/default.aspx<br />http://msmvps.com/blogs/brunoboucard<br />
  2. 2. Choisir une stratégie et un algorithme adaptés<br />Trouver la concurrence<br />Méthode itérative<br />
  3. 3. Etape 1<br />Mesurer les performances de la solution<br />
  4. 4. Etape 1Analyser les performances<br />Analyser les coûts de la solution séquentielle<br />
  5. 5. Analyser les performances<br />Démonstration<br />
  6. 6. Etape 2<br />Trouver la concurrence<br />
  7. 7. Etape 2Trouver la concurrence<br />Commencer avec un cahier des charges qui décrit le problème<br />Quel que soit le contexte fonctionnel ou technique, vous serez guidé naturellement par l’une des décompositions <br /><ul><li>Terminer avec une décomposition de tâches ordonnées en fonction des dépendances techniques et fonctionnelles, compléter par les données partagées que vous aurez identifiées
  8. 8. Il arrive qu’on passe d’une décomposition orientée tâches à une décomposition orientée données ou bien flux de données en fonction du type de traitement</li></ul>Commencer<br />Analyse des dépendances<br />Décomposition<br />Grouper les tâches<br />Orientée données<br />Ordonner les tâches<br />Evaluer le design <br />Orientée tâches<br />Partager les données<br />Analyser les dépendances<br /><ul><li> Visual Studio 2010 : Architecture -> GenerateDependency Graph
  9. 9. NDepend: http://www.ndepend.com/</li></li></ul><li>Trouver la concurrence<br />Démonstration<br />
  10. 10. Trouver la concurrenceSolution séquentielle<br />Dépiler un nom de fichier<br />WM_TIMER<br />Sélectionner un répertoire<br />Charger une image<br />Structure de pile partagée<br />Charger tous les noms d’images dans le répertoire et ses sous répertoires<br />Normaliser l’image<br />Twister  <br />l’image<br />Insérer dans le contrôle graphique<br />Empiler les noms de fichiers sélectionnés <br />Convertir l’image en une vignette<br />
  11. 11. Etape 2Décomposition et granularité<br />Votre décomposition en tâches doit tenir compte de leur granularité et de leur surcoût<br />Cœur 0<br />Cœur 1<br />Cœur 2<br />Cœur 3<br />Cœur 1<br />Cœur 2<br />Cœur 0<br />Cœur 3<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />Configuration 2<br />Quelle est la meilleure configuration ?<br />Configuration 1<br />surcoût<br />charge<br />
  12. 12. Etape 2Décomposition et répartition de charge<br />Votre groupement de tâches doit tenir compte de leur charge<br />Cœur 1<br />tâche<br />Cœur 2<br />Cœur 0<br />Cœur 3<br />Cœur 1<br />Cœur 2<br />Cœur 0<br />Cœur 3<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />tâche<br />Configuration 2<br />Quelle est la meilleure configuration ?<br />Configuration 1<br />surcoût<br />charge<br />
  13. 13. Etape 2Evaluer votre design<br />Flexibilité<br />Préférer l’abstraction pour faciliter l’adaptation à différents scénarios d’exécution<br />Nombre de cœurs sollicités<br />Partitionnement des données<br />Efficacité<br />Le temps dépensé à gérer le parallélisme vs le temps gagné à tirer parti des cœurs<br />Amélioration des performances en fonction du nombre de processeurs<br />Simplicité<br />Le code peut être facilement diagnostiqué<br />La solution technique choisie est facile à maintenir <br />
  14. 14. Etape 3<br />Choisir un algorithme en fonction de votre stratégie<br />
  15. 15. Etape 3Choisir un algorithme en fonction de votre stratégie<br />
  16. 16. Etape 3Algorithmes données<br />Décomposition géométrique<br />Taille des morceaux <br /><ul><li> Trop grand – sous utilisation
  17. 17. Trop petit – sur consommation </li></ul>Format des morceaux<br /><ul><li> Attention au false sharing </li></ul>Traitement d’une collection d’images, addition de matrices …<br />
  18. 18. ModéliserAlgorithmes données – Attention au False Sharing<br />Pour des raisons de performances, les systèmes utilisent des lignes de cache<br />Lorsque des threads sur différents processeurs modifient en parallèle les variables qui résident sur la même ligne de cache, le False Sharing n’est pas loin <br />Coeur 0<br />Coeur 1<br />T0<br />T1<br />Cache<br />Cache<br />Ligne de cache<br />Ligne de cache<br /><ul><li>Éviter l'allocation de mémoire contiguë
  19. 19. Deux champs d'instance dans la même instance de classe sont proches dans leurs emplacement de mémoire
  20. 20. Deux champs statiques dans le même type sont proches en mémoire
  21. 21. Deux éléments avec des index adjacents dans un tableau sont proches en mémoire
  22. 22. Les objets alloués consécutivement sont probablement proches en mémoire
  23. 23. Les variables locales utilisées ensemble dans une fermeture sont probablement capturées dans les champs d'instance, et ainsi, d'après le premier commentaire ci-dessus, sont également proches en mémoire</li></ul>Mémoire<br />
  24. 24. Algorithmes données: False Sharing<br />Démonstration<br />
  25. 25. Etape 3Décomposition géométrique<br />for (int i = 0; i < size; i++) {<br />Parallel.For(0, size, j => {<br />inttemp = 0;<br /> for (int k = 0; k < size; k++) {<br />temp += m1[i, k] + m2[k, j];<br /> }<br />result[i, j] = temp;<br /> });<br />}<br />
  26. 26. Etape 3Algorithmes données<br />Décomposition récursive<br />Le choix de la profondeur détermine la performance<br />- Arbre profond <br /><ul><li> contention processeurs</li></ul>- Arbre de profondeur limitée<br /><ul><li> sous utilisation des processeurs</li></ul>1 tâche<br />2 tâches<br />3 tâches<br />3 tâches<br />Parcours de graphe ou parcours d’arbre …<br />
  27. 27. Etape 3Récursivité sur des données<br />staticvoidWalk<T>(Tree<T> root, Action<T> Action)<br />{<br /> if (root == null) return;<br />var t1 = Task.Factory.StartNew(() =><br /> action(root.Data));<br />var t2 = Task.Factory.StartNew(() =><br />Walk(root.Left, action));<br />var t3 = Task.Factory.StartNew(() =><br />Walk(root.Rigth, action));<br />Task.WaitAll(t1, t2, t3);<br />}<br />
  28. 28. Etape 3Algorithmes tâches<br />Parallélisme de tâches linéaires<br />Nombre de tâches<br /><ul><li> trop peu: les cœurs sont sous utilisés
  29. 29. trop élevé: contention des tâches</li></ul>Dépendances<br /><ul><li> retirables
  30. 30. séparables
  31. 31. lecture seule ou lecture/écriture</li></ul>sous opération 1<br />sous opération 2<br />sous opération 3<br />sous opération 4<br />sous opération 4<br />sous opération 1<br />sous opération 2<br />Paralléliser des opérations décomposables<br />sous opération 3<br />
  32. 32. Etape 3Algorithmes tâches<br />Arbres profond <br /><ul><li> contention processeurs</li></ul>Arbres de profondeur limitée<br /><ul><li> sous utilisation des processeur</li></ul>Diviser pour régner<br />Problème<br />Séquentiel<br />Split<br />Sous - problème<br />Sous - problème<br />2 chemins parallèles<br />Split<br />Split<br />Sous - problème<br />Sous - problème<br />Sous - problème<br />Sous - problème<br />4 chemins parallèles<br />Résoudre<br />Résoudre<br />Résoudre<br />Résoudre<br />Sous - problème<br />Sous - problème<br />Sous - problème<br />Sous - problème<br />Fusionner<br />Fusionner<br />2 chemins parallèles<br />Sous - solution<br />Sous - solution<br />Fusionner<br />QuickSort<br />Solution<br />Séquentiel<br />
  33. 33. Etape 3Diviser pour régner<br />staticvoidQuickSort<T>(T[] data, intfromInclusive, inttoExclusive) where T : IComparable<T><br />{<br /> if (toExclusive - fromInclusive <= THRESHOLD)<br /> {<br />InsertionSort(data, fromInclusive, toExclusive);<br /> }<br />else<br /> {<br />intpivotPos = Partition(data, fromInclusive, toExclusive);<br /> if (toExclusive - fromInclusive <= PARALLEL_THRESHOLD)<br /> {<br /> // NOTE: PARALLEL_THRESHOLD ischosen to begreaterthan THRESHOLD.<br />QuickSort(data, fromInclusive, pivotPos);<br />QuickSort(data, pivotPos, toExclusive);<br /> }<br />elseParallel.Invoke(<br /> () => QuickSort(data, fromInclusive, pivotPos),<br /> () => QuickSort(data, pivotPos, toExclusive));<br /> }<br />}<br />
  34. 34. Etape 3Algorithmes flux de données<br />Pipeline<br />Les charges de travail des étapes<br /><ul><li> égales – pipeline linéaire
  35. 35. inégales – pipeline non-linéaire</li></ul>Chaîne de montage automobile<br />
  36. 36. Etape 3Algorithmes flux de données<br />Coordination orientée événements<br />Traitement d’une dépêche sur un desk journalistique …<br />
  37. 37. Etape 3Pipeline<br /> var input = new BlockingCollection<string>();<br /> var readLines = Task.Factory.StartNew(() => <br /> { <br />try { <br />foreach(var line in File.ReadAllLines(@"input.txt"))<br />input.Add(line); <br /> } <br />finally<br /> { <br />input.CompleteAdding(); <br /> } <br /> });<br /> var writeLines = Task.Factory.StartNew(() => <br /> { <br />File.WriteAllLines(@"output.txt", input.GetConsumingEnumerable()); <br /> });<br />Task.WaitAll(readLines, writeLines);<br />
  38. 38. Etape 4<br />Choisir un pattern de structure<br />
  39. 39. Etape 4Choisir un pattern de structures de programme <br />Après avoir sélectionné votre algorithme parallèle, il faut maintenant le supporter dans votre programme<br />Structures de programme<br />SPMD<br />Master/Worker<br />Boucle parallèle<br />Fork/Join<br />
  40. 40. Etape 4Les Patterns de structures de programme<br />SPMD, Master/Worker, Boucle parallèle et Fork/Join partagent les mêmes idiomes<br />Partitionner<br />Exécuter<br />Fusionner<br />
  41. 41. Etape 4Fork / Join<br />var tasks = new Task[3];<br />tasks[0] = new Task(() => ComputeMean());<br />tasks[1] = new Task(() => ComputeMedian());<br />tasks[2] = new Task(() => ComputeMode());<br />foreach(Task t in tasks) t.Start(); <br />Task.WaitAll(tasks);<br />Parallel.Invoke(<br />() => ComputeMean(),<br /> () => ComputeMedian(),<br /> () => ComputeMode()<br />);<br />
  42. 42. Etape 4Boucle parallèle<br />Parallel.ForEach(stack, <br /> bitmap => _imagesStack.Push(ProcessBitmap(bitmap)));<br /> var query = from bitmap in stack.AsParallel() <br /> select ProcessBitmap(bitmap);<br />query.ForAll(image => _imagesStack.Push(image));<br />
  43. 43. Etape 4Structures de programme parallèle<br />Appliquées dans différents contextes<br />SPMD – Systèmes distribués<br />MPI, SOA, GridComputing,<br />Fork / Join – Orienté Tâches<br />TPL<br />Master/Worker – Orienté Tâches<br />TPL<br />Boucle Parallèle – Orienté Données <br />TPL, PLINQ<br />
  44. 44. Structures de programme parallèle<br />Démonstration<br />
  45. 45. Structures de programme parallèleSolution parallèle<br />WM_TIMER<br />Collection des noms de fichiers<br />Sélectionner un répertoire<br />Pile partagée concurrente<br />Charger une image<br />Charger tous les noms d’images dans le répertoire et ses sous répertoires<br />Dépiler une vignette<br />Normaliser l’image<br />Insérer dans le contrôle graphique<br />Twister l’image<br />Empiler les noms de fichiers sélectionnés <br />Convertir l’image en une vignette<br />
  46. 46. Etape 1<br />Mesurer les performances de la solution<br />
  47. 47. Etape 1Analyser les performances<br />Analyser les coûts de la solution parallèle<br />
  48. 48. Structures de programme parallèle<br />Démonstration<br />
  49. 49. ConclusionQuelques suggestions pour l’implémentation<br />Préférer les nouveaux outils de haut niveau d’abstraction<br />Privilégier les solutions simples à maintenir<br />La granularité fine est souvent synonyme de complexité<br />Utiliser systématiquement des librairies thread-safe<br />Fiabilité == Gain de temps<br />Ne jamais présumer d’un ordre d’exécution<br />La justesse du code est à ce prix<br />
  50. 50. ConclusionPour retrouver le « Free Lunch »<br />Respecter les 4 grandes étapes<br />Notamment les « analyses des dépendances » et « analyses des performances »<br />Avancer de manière itérative<br />Difficile de trouver « la solution » du premier coup<br />Si possible privilégier les capacités de monter en charge ainsi que la simplicité <br />Mesurer régulièrement les performances de vos choix<br />Visual Studio 2010 est votre ami<br />Si vos choix ne vous semblent pas satisfaisants<br />Oser changer votre algorithme pour augmenter ses chances de parallélisation<br />Penser Parallèle<br />C’est en pratiquant régulièrement la méthode présentée que vous gagnerez en réflexes sur l’usage des Patterns parallèles les mieux adaptés à vos besoins<br />
  51. 51. Vous n’êtes pas seul !Livres et blogues<br />Mes ouvrages préférés<br />Programmation Parallèle<br />http://msmvps.com/blogs/brunoboucard<br />Portail Microsoft ParallelComputing<br />http://msdn.microsoft.com/en-us/concurrency<br />Patterns for Parallel Programming de Stephen Toub<br />http://www.microsoft.com/downloads/details.aspx?FamilyID=86b3d32b-ad26-4bb8-a3ae-c1637026c3ee&displaylang=en<br />

×