299 lines
17 KiB
TeX
299 lines
17 KiB
TeX
\section{Performances}
|
|
|
|
%{{{ Introduction "
|
|
Cette section présente des mesures de temps d'exécution de programmes utilisant la bibliothèque
|
|
présentée dans ce chapitre.
|
|
Toutes les mesures ont été effectuées sur une machine dotée d'un Intel Xeon E7-8890 v3, cadencé à
|
|
\SI{2.5}{\GHz} et ayant \num{72} cœurs physiques (\num{18} processeurs ayant chacun \num{4} cœurs
|
|
physiques).
|
|
Les programmes sont compilés en utilisant GCC (\texttt{g++}) 8.2.0 avec notamment le pack
|
|
d'optimisations \texttt{O2}.
|
|
Les valeurs données dans ce document sont obtenues par une moyenne sur \num{20} exécutions en
|
|
utilisant des états initiaux différents pour les \gls{PRNG} (les mêmes \num{20} états sont utilisés
|
|
à chaque fois).
|
|
Lorsque cela est pertinent (notamment, suffisamment visible), l'intervalle de confiance à
|
|
\SI{99}{\percent} est affiché.
|
|
Pour ce qui est des exécutions, nous avons réglé l'affinité du processus de sorte que pour chaque
|
|
processeur, au maximum un cœur soit utilisé.
|
|
Cela limite donc à \num{18} cœurs pour protéger d'un biais de mesure potentiel dû à l'utilisation
|
|
simultanée de plusieurs cœurs d'un même processeur.
|
|
|
|
Un objectif important que devait atteindre cette bibliothèque est d'être compétitive, en termes de
|
|
temps d'exécution par rapport à une solution écrite directement par un développeur.
|
|
En effet, une abstraction est moins intéressante si elle implique un fort surcoût.
|
|
Pour cette raison, nous avons utilisé le langage C++ et la métaprogrammation template qui
|
|
permettent, lorsqu'ils sont correctement utilisés, de s'approcher d'une implémentation manuelle en
|
|
ce qui concerne le temps d'exécution.
|
|
|
|
Pour évaluer cela, nous avons comparé les temps d'exécution de programmes écrits en utilisant la
|
|
bibliothèque (et en utilisant différentes politiques d'exécution) par rapport aux temps d'exécution
|
|
de programmes équivalents écrits sans utiliser cette bibliothèque.
|
|
Ces programmes implémentent un \graspels{} dans le but de résoudre des instances de \gls{TSP} de
|
|
\num{38} sommets et de \num{194} sommets, ils utilisent donc des nombres pseudo-aléatoires.
|
|
Afin de s'assurer de la pertinence des temps mesurés, nous avons donc fait en sorte de garantir la
|
|
répétabilité des programmes écrits sans utiliser la bibliothèque.
|
|
Pour les versions l'utilisant, la répétabilité est obtenue sans effort.
|
|
|
|
Dans cette section, nous utiliserons trois variables $N$, $O$ et $I$ qui correspondront
|
|
respectivement au nombre d'itérations (parallélisables) du \gls{GRASP}, au nombre d'itérations (non
|
|
parallélisables) de la boucle extérieure de l'\gls{ELS} et au nombre d'itérations (parallélisables)
|
|
de la boucle intérieure de l'\gls{ELS}.
|
|
|
|
Toutes les mesures sont effectuées pour quatre politiques d'exécutions différentes lorsque la
|
|
bibliothèque est utilisée :
|
|
\begin{itemize}
|
|
\item \og firstlevel \fg, qui ne parallélise que le premier niveau pouvant être exécuté en
|
|
parallèle ;
|
|
\item \og staticpool \fg, qui utilise un \en{thread pool} en associant à une tâche un
|
|
\en{thread} en fonction de son identifiant et en équilibrant les multiples niveaux parallèles
|
|
comme décrit dans \acref{sec:alsk/exec} ;
|
|
\item \og dynamicpool \fg, qui utilise un \en{thread pool} utilisé de manière classique ;
|
|
\item \og thread \fg, qui applique l'équilibrage des multiples niveaux parallèles de
|
|
\acref{sec:alsk/exec} en créant des \en{threads} dynamiquement.
|
|
\end{itemize}
|
|
%}}}
|
|
|
|
%{{{ Sequential "
|
|
Bien que l'objectif principal de cette bibliothèque soit d'aider à l'écriture de programmes
|
|
parallèles, elle peut également être utilisée pour produire des programmes séquentiels en
|
|
choisissant une politique d'exécution appropriée.
|
|
Cela peut par exemple être utile à des fins de débugage, mais aussi pour effectuer des mesures de
|
|
performance dans certains domaines scientifiques où les comparaisons sont généralement effectuées
|
|
sur des programmes séquentiels.
|
|
Nous avons donc dans un premier temps mesuré les temps d'exécution de programmes séquentiels.
|
|
|
|
\Acref{fig:alsk/results/rt_graspels_dj38_24_20_20_seq,fig:alsk/results/rt_graspels_qa194_24_20_20_seq}
|
|
montrent les temps d'exécution de programmes séquentiels avec $N = 24$, $O = 20$ et $I = 20$.
|
|
Les étiquettes préfixées de \og hw \fg{} correspondent aux programmes écrits sans utiliser la
|
|
bibliothèque tandis que celles préfixées de \og sk \fg{} indiquent ceux l'utilisant.
|
|
L'étiquette \og hw\_seq \fg{} est associée au programme écrit pour une exécution séquentielle.
|
|
Quant à \og hw\_par \fg{}, il s'agit d'un programme écrit pour une exécution parallèle, bien que
|
|
l'exécution sera effectivement séquentielle puisqu'un seul cœur est affecté.
|
|
|
|
Quelle que soit la politique d'exécutions utilisée pour les versions employant les squelettes
|
|
algorithmiques, l'exécution sera également obligatoirement séquentielle en pratique puisqu'un seul
|
|
cœur est affecté comme pour \og hw\_par \fg{}.
|
|
|
|
\Acref{fig:alsk/results/rt_graspels_dj38_24_20_20_seq} correspond aux temps d'exécution pour une
|
|
instance de \gls{TSP} ayant \num{38} sommets et il s'agit d'une instance ayant \num{194} sommets
|
|
pour \acref{fig:alsk/results/rt_graspels_qa194_24_20_20_seq}.
|
|
Pour une petite instance (\num{38} sommets), on observe de légères variations, cependant la courte
|
|
durée d'exécution ne permet pas de conclure clairement, sinon que l'utilisation de la bibliothèque
|
|
n'engendre pas de surcoût significatif même pour des tâches courtes.
|
|
|
|
Les temps mesurés pour la seconde instance (\num{194} sommets) sont particulièrement proches pour
|
|
les programmes écrits sans et avec la bibliothèque à l'exception des politiques d'exécution \og
|
|
staticpool \fg{} et \og thread \fg.
|
|
Ainsi, à nouveau, on observe que la politique d'exécution séquentielle permet d'obtenir des
|
|
exécutions de durées quasiment identiques à ce que l'on peut atteindre sans utiliser la
|
|
bibliothèque.
|
|
À l'inverse, l'utilisation d'une autre politique d'exécution dans un contexte séquentiel (parce
|
|
qu'un seul cœur est disponible) peut en revanche être coûteux.
|
|
En effet, celles-ci peuvent avoir besoin de mettre en place des mécanismes, lesquels ne sont
|
|
généralement pas optimisés pour le cas d'une exécution séquentielle.
|
|
|
|
Afin de garantir la répétabilité y compris lorsqu'un seul \en{thread} est possible, l'implémentation
|
|
des politiques d'exécution doit dans ce cas également produire un comportement cohérent, privant
|
|
celle-ci d'éventuelles optimisations.
|
|
Cela explique par ailleurs pourquoi la bibliothèque n'utilise pas automatiquement la politique
|
|
d'exécution séquentielle lorsque le nombre de cœurs alloués est de \num{1}.
|
|
Néanmoins, dans le cas où la répétabilité n'a pas besoin d'être assurée au point d'avoir le même
|
|
déroulement entre une exécution séquentielle et une exécution parallèle, alors il est avisé de la
|
|
part du développeur d'utiliser la politique d'exécution séquentielle lorsqu'il n'y a qu'un cœur.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_dj38_24_20_20_seq.pdf}
|
|
\caption{Temps d'exécution séquentielle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{38} sommets}
|
|
\label{fig:alsk/results/rt_graspels_dj38_24_20_20_seq}
|
|
\end{figure}
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_24_20_20_seq.pdf}
|
|
\caption{Temps d'exécution séquentielle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{194} sommets}
|
|
\label{fig:alsk/results/rt_graspels_qa194_24_20_20_seq}
|
|
\end{figure}
|
|
%}}}
|
|
|
|
%{{{ Parallel "
|
|
%{{{ var cores "
|
|
\Acref{fig:alsk/results/rt_graspels_dj38_24_20_20_par,fig:alsk/results/rt_graspels_qa194_24_20_20_par}
|
|
présentent les temps d'exécution de programmes parallèles avec $N = 24$, $O = 20$ et $I = 20$.
|
|
La première étiquette, \og hw\_par \fg, correspond au programme écrit sans utiliser la
|
|
bibliothèque pour une exécution parallèle.
|
|
Les étiquettes préfixées par \og sk \fg{} correspondent à des programmes écrits en utilisant la
|
|
bibliothèque et en utilisant différentes politiques d'exécution.
|
|
La seconde partie de chaque étiquette indique quelle politique d'exécution est utilisée.
|
|
|
|
Pour une instance de \gls{TSP} ayant \num{38} sommets, on obtient les résultats présentés par
|
|
\acref{fig:alsk/results/rt_graspels_dj38_24_20_20_par}.
|
|
On observe globalement des performances similaires et surtout une décroissance stable du temps
|
|
d'exécution avec l'augmentation du nombre de cœurs.
|
|
La politique d'exécution \og staticpool \fg{} semble particulièrement efficace.
|
|
Cela se justifie par l'absence de sections critiques (contrairement à ce que l'on a avec \og
|
|
dynamicpool \fg) et la réutilisation des \en{threads} créés (contrairement à ce que fait \og
|
|
thread \fg).
|
|
La répartition des tâches est également un peu meilleure que celle de \og firstlevel \fg{} pour les
|
|
dernières itérations de la boucle principale du \gls{GRASP} lorsqu'il y a un reste à la division de
|
|
$N$ par le nombre de cœurs disponibles.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_dj38_24_20_20_par.pdf}
|
|
\caption{Temps d'exécution parallèle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{38} sommets}
|
|
\label{fig:alsk/results/rt_graspels_dj38_24_20_20_par}
|
|
\end{figure}
|
|
|
|
Lorsque l'on travaille sur une instance de \gls{TSP} avec \num{194} sommets, on observe les
|
|
résultats présentés dans \acref{fig:alsk/results/rt_graspels_qa194_24_20_20_par}.
|
|
Ils sont dans l'ensemble équivalents aux précédents, ce qui confirme une certaine indépendance des
|
|
performances obtenues par rapport à la taille des données traitées.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_24_20_20_par.pdf}
|
|
\caption{Temps d'exécution parallèle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{194} sommets}
|
|
\label{fig:alsk/results/rt_graspels_qa194_24_20_20_par}
|
|
\end{figure}
|
|
%}}}
|
|
|
|
%{{{ var grasp_n "
|
|
En faisant varier $N$ de \num{4} à \num{20} par pas de \num{4}, on obtient les courbes de
|
|
\acref{fig:alsk/results/rt_graspels_qa194_4:20_20_20_par}.
|
|
En particulier pour $N=4$ (\cref{fig:alsk/results/rt_graspels_qa194_4_20_20_par}), $N=8$
|
|
(\cref{fig:alsk/results/rt_graspels_qa194_8_20_20_par}) et $N=12$
|
|
(\cref{fig:alsk/results/rt_graspels_qa194_12_20_20_par}) pour lesquels c'est très visible, on
|
|
observe une limite dans l'accélération obtenue pour \og firstlevel \fg.
|
|
Ce résultat est logique puisque la politique d'exécution utilisée dans ce cas ne parallélisant que
|
|
le premier niveau possible, si celui-ci correspond à une boucle de $k$ itérations l'accélération
|
|
maximale que l'on peut obtenir est de $k$.
|
|
Les autres politiques d'exécution se comportent de la même manière, indépendamment du nombre
|
|
d'itérations du \gls{GRASP}, ce qui est attendu puisque toutes les trois sont capables de
|
|
paralléliser de multiples niveaux et donc de tirer profit des itérations parallélisables de
|
|
l'\gls{ELS} (au nombre fixe de $I=20$).
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\foreach \n in {4,8,...,20} {
|
|
\begin{subfigure}[b]{.49\textwidth}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_v\n_20_20_par.pdf}
|
|
\caption{$N = \n$}
|
|
\label{fig:alsk/results/rt_graspels_qa194_\n_20_20_par}
|
|
\end{subfigure}
|
|
\hfill
|
|
}
|
|
\begin{subfigure}[b]{.49\textwidth}
|
|
\centering
|
|
\hspace{2em}
|
|
\includegraphics{img/alsk/rt_graspels_qa194_var_grasp_par_legend.pdf}
|
|
\vspace{12ex}
|
|
\end{subfigure}
|
|
\caption{Temps d'exécution parallèle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{194} sommets selon
|
|
le nombre d'itérations du \glsxtrshort{GRASP}}
|
|
\label{fig:alsk/results/rt_graspels_qa194_4:20_20_20_par}
|
|
\end{figure}
|
|
%}}}
|
|
|
|
%{{{ var els iter max "
|
|
Nous avons voulu vérifier l'effet de la variation du nombre d'itérations de la boucle centrale non
|
|
parallélisable (la boucle extérieure de l'\gls{ELS} dont le nombre d'itérations est $O$).
|
|
Les courbes présentées dans \acref{fig:alsk/results/rt_graspels_qa194_4_1:50_20_par} correspondent à
|
|
des mesures de temps d'exécution pour $N=4$ et $O$ variant entre $1$ et $50$.
|
|
Ces courbes permettent d'observer que d'une manière globale, le comportement reste similaire.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\foreach \n in {1,4,8,40,50} {
|
|
\begin{subfigure}[b]{.49\textwidth}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_4_v\n_20_par.pdf}
|
|
\caption{$O = \n$}
|
|
\label{fig:alsk/results/rt_graspels_qa194_4_\n_20_par}
|
|
\end{subfigure}
|
|
\hfill
|
|
}
|
|
\begin{subfigure}[b]{.49\textwidth}
|
|
\centering
|
|
\hspace{2em}
|
|
\includegraphics{img/alsk/rt_graspels_qa194_var_els_iter_max_par_legend.pdf}
|
|
\vspace{12ex}
|
|
\end{subfigure}
|
|
\caption{Temps d'exécution parallèle d'un \graspels{} pour une instance de \glsxtrshort{TSP} de \num{194} sommets selon
|
|
le nombre d'itérations non parallélisables de l'\glsxtrshort{ELS}}
|
|
\label{fig:alsk/results/rt_graspels_qa194_4_1:50_20_par}
|
|
\end{figure}
|
|
%}}}
|
|
|
|
%{{{ speedup "
|
|
Enfin, sur la base des données précédentes,
|
|
\acref{fig:alsk/results/rt_graspels_qa194_4_20_20_speedup,fig:alsk/results/rt_graspels_qa194_20_20_20_speedup}
|
|
présentent l'accélération obtenue pour les différentes politiques d'exécution (ainsi que pour une
|
|
parallélisation sans utiliser la bibliothèque) en fonction du nombre de cœurs alloués.
|
|
\Acref{fig:alsk/results/rt_graspels_qa194_4_20_20_speedup} correspond à l'exécution d'un \graspels{}
|
|
pour lequel $N=4$, c'est-à-dire que la boucle principale du \gls{GRASP} effectue $4$ itérations qui
|
|
peuvent être parallélisées.
|
|
Les valeurs de $O$ et $I$ sont conservées à la valeur par défaut utilisée durant cette section, à
|
|
savoir $20$ pour les deux.
|
|
Lorsque $N=4$, on observe en particulier que la parallélisation en utilisant une politique
|
|
d'exécution ne traitant qu'un niveau est limitée en accélération à la valeur atteinte lorsque le
|
|
nombre de cœurs alloués égale $N$, ce qui n'est pas surprenant.
|
|
Par ailleurs, l'accélération obtenue en utilisant la politique d'exécution par \en{thread pool} \og
|
|
statique \fg{} semble être généralement meilleure que les autres.
|
|
Dans tous les cas, on observe que l'accélération croît effectivement lorsque le nombre de cœurs
|
|
alloués croît.
|
|
|
|
\begin{figure}
|
|
\ocgfigIG{Accélération en fonction du nombre de coeurs alloués}
|
|
{results/rt_graspels_qa194_4_20_20_speedup}{rt_graspels_qa194_}{_20_20_speedup.pdf}
|
|
{Accélération en fonction du nombre de cœurs alloués (\graspels{} avec $N=4$, \glsxtrshort{TSP} de \num{194} sommets)}
|
|
{$N$ :}{\N}{4/on,8/off,12/off,16/off}
|
|
\end{figure}
|
|
|
|
\Acref{fig:alsk/results/rt_graspels_qa194_20_20_20_speedup} correspond quant à elle à l'exécution
|
|
d'un \graspels{} avec $N=20$.
|
|
Le comportement global est conservé et l'accélération obtenue est proportionnelle au nombre de cœurs
|
|
alloués.
|
|
Pour la politique d'exécution \og sk\_firstlevel \fg{}, l'accélération obtenue n'atteint logiquement
|
|
plus un plafond puisque le nombre de cœurs alloués n'atteint pas, durant ces mesures, la valeur de
|
|
$N$.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_20_20_20_speedup.pdf}
|
|
\caption{Accélération en fonction du nombre de cœurs alloués (\graspels{} avec $N=20$,
|
|
\glsxtrshort{TSP} de \num{194} sommets)}
|
|
\label{fig:alsk/results/rt_graspels_qa194_20_20_20_speedup}
|
|
\end{figure}
|
|
|
|
\Acref{fig:alsk/results/rt_graspels_qa194_all_20_20_speedup_1,fig:alsk/results/rt_graspels_qa194_all_20_20_speedup_16}
|
|
montrent l'accélération obtenue en fonction de la valeur de $N$ pour un nombre de cœurs alloués
|
|
défini, respectivement \num{1} et \num{18}.
|
|
Dans \acref{fig:alsk/results/rt_graspels_qa194_all_20_20_speedup_1}, on observe donc le comportement
|
|
des politiques d'exécution lorsqu'elles sont utilisées pour une exécution séquentielle.
|
|
Cela confirme les remarques précédentes à propos de la politique \og sk\_staticpool \fg{} qui
|
|
apparaît comme étant la moins efficace dans ce contexte précis.
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\includegraphics{img/alsk/rt_graspels_qa194_all_20_20_speedup_1.pdf}
|
|
\caption{Accélération en fonction de $N$ pour \num{1} cœur alloué (\graspels{}, \glsxtrshort{TSP} de \num{194} sommets)}
|
|
\label{fig:alsk/results/rt_graspels_qa194_all_20_20_speedup_1}
|
|
\end{figure}
|
|
|
|
En revanche, dans \acref{fig:alsk/results/rt_graspels_qa194_all_20_20_speedup_16} on vérifie que
|
|
pour toute valeur de $N$ cette politique d'exécution est meilleure.
|
|
Cela semble indiquer que le fait d'effectuer une partie du travail durant la compilation et de
|
|
déterminer à l'avance à quel \en{thread} affecter chaque tâche affecte favorablement les
|
|
performances du programme.
|
|
|
|
\begin{figure}
|
|
\ocgfigIG{Accélération en fonction du nombre d'itérations}
|
|
{results/rt_graspels_qa194_all_20_20_speedup_16}{rt_graspels_qa194_all_20_20_speedup_}{.pdf}
|
|
{Accélération en fonction de $N$ pour \num{18} cœurs alloués (\graspels{}, \glsxtrshort{TSP} de \num{194} sommets)}
|
|
{nombre de cœurs :}{\T}{2/off,4/off,6/off,8/off,10/off,12/off,14/off,16/off,18/on}
|
|
\end{figure}
|
|
%}}}
|
|
%}}}
|