%\part{Cours 1\er} \chapter{Introduction} \section{Motivations} On augmente plus trop en fréquence, ça augmente plus trop ces dernières années comparé à avant. Et pourtant, on augmente toujours le nombre de processeurs sur les puces. On va de plus en plus vers du parallélisme et non plus vers de la vitesse. \section{Vocabulaire} \subsection{Modèle d'exécution} \subsubsection{À acteurs} Ils communiquent entre-eux~: au lieu d'éxécuter le code d'un objet dans le thread courant, la méthode s'éxécute dans le thread de l'objet. Similaire au RPC. \subsubsection{Futures} Notion d'éxécution lazy. Le calcul commence en tâche de fond, lorsque l'on a besoin de la valeur, on est mis en attente si elle n'est pas encore calculée~; on a donc pas à attendre au début du calcul. \subsubsection{CSP -- Communication Séquential Processors} Modèle dans lequel on a des processus séquentiel qui ne font que se parler (pas de mémoire partagée). \subsubsection{CCS} ? \subsection{Gestion des sections critiques} \subsubsection{Locks} \subsubsection{Conditions} \subsubsection{Mutex} \subsubsection{Sémaphores} \subsection{Histoire} Apparu vers la fin des années 50. Premier aboutissement (machine à 4 processeurs) en 1962. \subsection{Définitions} \subsubsection{Matérielle} \paragraph{Symetric MultiProcessor} On place plusieurs processeurs (identiques ou non) sur une carte mère spéciale. \paragraph{Multi-core} deux processeurs ou plus en un, avec certain composants partagés~: en particulier les caches. Moins cher que du SMP \paragraph{Hyper-threading} Étant donné que chaque famille d'instruction a une zone réservée dans le processeur, du coup, lorsqu'une instruction est en train de s'exécuter, elle bloque le reste du processeur, du coup, beaucoup de circuit ne servent pas, l'hyperthreading essaye d'exécuter plusieurs zone en même temps. \paragraph{Vectorized instructions} applique une instruction sur un lot de donnée. \paragraph{Non-Uniform Memory Access (\textsc{numa})} on associe à chaque processeur des blocs mémoires. Un certain processeur a un accès prioritaire à ses blocs mémoires. \chapter{Soyons parallèle} \section{Gain~?} Amdahl's law~: $$\frac{1}{(1-P\frac{P}{N}}$$ On n'ira jamais deux fois plus vite que deux fois plus vite avec 50\% du code parallélisé. Au final, il faut s'arranger sur la parallélisation du code, plutôt que sur le nombre de processeurs.\\ Gustafson's law~: $$S(P)=P+a\times(P-1)$$ \section{Différents types de parallélisme} \subsection{Décomposition par tâches} On lance un thread par tâche en gros. Il faut voir les problèmes d'interdépendances entre chaque thread, etc. \subsection{Data driven} On a un paquet de données similaire. On a un traitement similaire à effectuer sur ces données. On découpe les données en petits blocs et on lance plein de petit threads. \subsection{Modèle flow} Comme une chaîne de production. On a $n$ tâche, chaque unité traite une tâche. Pour que ça marche, il faut que les données soient correctement découpés. \subsection{Design Patterns} \paragraph{Divide and Conquer} on divise le problème, puis on a une seconde phase de recollection des données. \paragraph{Pipeline} mise en pratique de la décomposition par data flow. \paragraph{Wave front} mise en œuvre d'un tri topologique parallélisée. On traite une tâche quand on a traitée ses prédécesseurs. \paragraph{Geometric decomposition} façon de couper les blocs de données qui ne sont pas forcément contigus. \chapter{Interragir avec le cache du CPU} Le cache est nécessaire du fait que la mémoire est beaucoup plus lente que le processeur. Le cache est géré par ligne~: de quelques dizaine d'octets. Chaque ligne a un état par rapport à sa cohérence. Lorsqu'elle est invalide cela signifie qu'une modification a été effectuée en mémoire ou dans un autre cache (d'un autre processeur par exemple). En état partagé, il n'est pas possible d'écrire dessus, on peut seulement la lire. En mode exclusif, seul un processeur peut écrire la zone mémoire, elle est ensuite noté modified. \section{False sharing} Par moment, il peut y avoir deux processeurs qui utilisent deux valeurs différentes mais dans des lignes de cache identiques. Du coup il va invalider une ligne de cache sur l'autre processeur alors qu'en réalité, la valeur qui intéressait le processeur n'a pas été modifiée. On parle de \emph{False sharing}. Pour éviter ce genre de chose, il faut utiliser le plus de variable locale possible dans chaque thread, et avoir le moins de données communes.\\ \section{Memory fence} \chapter{Exclusion mutuelle} Une section critique est une section de code où l'on manipule des données partagées (quand on dépend d'événement d'écriture sur une zone mémoire). \section{Problème classique~: compteur partagé} On a un compteur partagé entre deux threads. \paragraph{Exclussion mutuelle~:} deux threads ne doivent pas pouvoir être au même endroit en même temps. \paragraph{Progression~:} lorsque l'on veut aller dans une section critique, on ne doit attendre que parce que quelqu'un y est déjà. \paragraph{Attente bornée~:} quand on attend la section critique. On attend un nombre fini de thread. \paragraph{Deadlock~:} mauvaise combinaison d'accès du coup deux thread s'attendent mutuellement. \paragraph{Race condition~:} sous certaines conditions d'éxécution, certaines propriétés ne sont pas respectées. \paragraph{Famine~:} un thread est en famine quand il attend indéfiniment une ressources. \chapter{Techniques de lock} \section{Comment locker~?} \subsection{Bloquer les interruptions et la mémoire} \subsubsection{Interruptions} Dans le code on déclenche des interruptions de temps en temps pour changer de thread. \subsubsection{Mémoire} On bloque un espace mémoire le temps de faire une série d'opération. \subsection{Test and set} On swap 1 avec le contenu d'une adresse mémoire. Ensuite on lit le contenu récupéré. S'il vaut 1, on continue de boucler. S'il vaut 0, tout autre thread obtiendrait un 1 du fait que l'opération de swap est atomique. \subsection{Compare and swap} On lui donne une adresse, une valeur de test, une nouvelle valeur. Exemple~: $+=$, $-=$, \ldots \subsection{Mutex} C'est un verrou que l'on utilise comme valeur abstraite qui correspond à une porte de toilette. C'est implémenté à base de compare and swap. \newline Pthread n'utilise pas des mutex équitables, ni implémentés sous forme de pile, ils sont réentrants et sont en attente passive. \newline L'attente passive c'est le fait de donner au kernel la charge de réveiller le thread lorsqu'un autre syscall indiquant qu'il a fini d'exécuter le code critique. Du fait du nombre de syscall, il est intéressant de garder un spin lock plutôt qu'un mutex. \subsection{Barrière} C'est un outil de synchronisation. Elle reste fermée tant qu'il n'y a pas assez de monde pour rentrer. La barrière se referme derrière le dernier. Peut utilisé, sauf avec Cuda sur les cartes graphiques. \subsection{Read/Write lock} Répond au problème du lecteur/rédacteur. Des thread lisent la zone mémoire et d'autre écrivent la zone mémoire. Lorsqu'il n'y a pas d'écriture, on peut lire sans gérer les locks. Mais lorsqu'il y a de l'écriture, il peut arriver que l'on lise le début de la réécriture puis l'ancien contenu écrit. On a donc deux mutex~: un pour la lecture, un autre pour l'écriture. Si le mutex d'écriture est actif, on attend systématique, si le mutex de lecture est actif, on autorise les autres lecteure, mais on attend pour faire les écritures. \subsection{Variable de condition} On associe une condition à un mutex. On boucle sur la condition, et on wait (sans oublier de libérer le mutex) tant que la condition n'est pas valide. \subsection{Sémaphores} On a un compteur protégé que l'on peut incrémenté ou décrémenter. Quand ce compteur est supérieur ou égal à 0~: le sémaphore est réveillé. Lorsqu'il est à 0 et qu'il est décrémenté, il se met en sommeil. Généralement, on utiliser les opérations P et V. P incrémente le compteur et V le décrémente. Par défaut, on a une notion de pile. \subsection{Moniteur} L'avantage des moniteurs est de cacher l'utilisation des wait Cela requiert d'avoir un langage objet. \section{Le diner des philosophes} On a 5 philosophes. \section{Structure de données} Les B-tree sont très adaptés au parallélisme. Les listes doublement chaînées sont à l'inverse, le pire choix possible. \subsection{Structures non bloquantes}