This repository has been archived on 2021-03-01. You can view files and clone it, but cannot push or open issues or pull requests.
cours-ing1/prpa/cours.tex

279 lines
8.5 KiB
TeX

%\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}