You are on page 1of 90

République Algérienne Démocratique Et Populaire

Université d’Oran
Faculté des Sciences
Département D’Informatique

Implémentation de méthodes de recherches


locales sur les architectures multi et many-cœurs.
Application au problème d’affectation
quadratique à 3 dimension.
THÈSE
présentée et soutenue publiquement
pour obtenir le

Diplôme de Magister

par

Mehdi Rouan Serik


Encadreur : Mr B.Yagoubi (Maître de conférences, Université d’Oran)
Co-encadreur : Mr L.Loukil (Maître de conférences, Université d’Oran)

Devant les membres du jury :

Président : Mr R. Nourine (MCA, Université d’Oran)

Examinateurs :
Mr Y. Lebbah (Professeur, Université d’Oran)
Mr A. Benyamina (MCA, Université d’Oran
i

Remerciements

Je tiens à remercier Dieu tout puissant qui m’a donné le courage et le soutien
pendant toutes mes années d’études.

Je tiens à remercier sincèrement Monsieur L Loukil en tant que co-encadreur


scientifique qui s’est toujours montré à l’écoute et étais très disponible tout au long
de la réalisation de ce mémoire, ainsi pour l’inspiration, l’aide et le temps qu’il a bien
voulu me consacrer et sans qui ce mémoire n’aurait jamais vu le jour et Monsieur B
Yagoubi, qui, en tant qu’encadreur administratif, a accpeter d’encadrer cet humble
travail.

J’exprime ma gratitude à Monsieur R. Nourine président de jury pour avoir


accepter de juger ce travail, ainsi que Monsieur Y. Lebbah et Monsieur A. Benyamina
les examinateurs de ce travail.

Enfin, j’adresse mes plus sincères remerciements à tous mes proches et amis, qui
m’ont toujours soutenue et encouragée au cours de la réalisation de ce mémoire.

Merci à tous et à toutes.


ii

Je dédie cette thèse à mes parents ...


mon frère ...
ma grand-mère et mes oncles ...
Sommaire

Résumé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1 Introduction générale 3

2 Le problème d’affectation quadratique à 3 dimension (Q3AP) 6


2.1 Les problèmes d’optimisation combinatoire . . . . . . . . . . . 7
2.2 Quelques exemples de problèmes d’optimisation combinatoire 8
2.3 Le problème d’affectation quadratique à 3 dimensions Q3AP 9
2.3.1 Définition du problème QAP . . . . . . . . . . . . . . . . 10
2.3.2 Définition du problème Q3AP . . . . . . . . . . . . . . . 11

3 Méthodes de résolution des problèmes combinatoires 13


3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Méthodes de résolution . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Méthodes exactes . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 Méthode "Branch and Bound" (B & B) . . . . . . . . . 15
3.3.2 Programmation dynamique . . . . . . . . . . . . . . . . . 16
3.4 Les métaheuristiques . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4.1 Définitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.4.2 Métaheuristique à population de solution . . . . . . . . 18
3.4.3 Métaheuristique à solution unique . . . . . . . . . . . . . 22
3.5 Métaheuristiques hybrides . . . . . . . . . . . . . . . . . . . . . . 27
3.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4 Les architecture multi et many cœurs 29


4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2 Les architectures parallèles et multi-cœurs . . . . . . . . . . . . 30
4.2.1 Classification des architectures . . . . . . . . . . . . . . . 30
4.2.2 Architectures multi-cœurs . . . . . . . . . . . . . . . . . . 33
4.3 Modèle de programmation parallèle . . . . . . . . . . . . . . . . 33
4.3.1 OpenMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

iii
SOMMAIRE iv

4.3.2 Message Passing Interface (MPI ) . . . . . . . . . . . . . 36


4.4 Les accélérateurs Graphiques GPU s . . . . . . . . . . . . . . . 38
4.4.1 Le GPGPU . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.5 BrookGPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.6 OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.7 CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.7.1 Contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.7.2 Modèle de programmation CUDA . . . . . . . . . . . . . 43
4.7.3 Parallélisme de donnée . . . . . . . . . . . . . . . . . . . . 43
4.7.4 Structure d’un programme CUDA . . . . . . . . . . . . 44
4.7.5 Mémoires périphériques et transfert de données . . . 45
4.7.6 Les kernels et les threads . . . . . . . . . . . . . . . . . . 46
4.7.7 Exemple d’un programme CUDA . . . . . . . . . . . . . 48
4.8 conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5 Conception de la solution du problème Q3AP 51


5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.2 Méthode de résolution du Q3AP adoptée . . . . . . . . . . . . 52
5.3 Solution du Q3AP et fonction d’évaluations . . . . . . . . . . . 53
5.3.1 Solution du Q3AP . . . . . . . . . . . . . . . . . . . . . . 53
5.3.2 Fonction d’évaluation du Q3AP . . . . . . . . . . . . . . 54
5.3.3 Fonction de voisinage . . . . . . . . . . . . . . . . . . . . . 55
5.4 La nécessité du parallélisme . . . . . . . . . . . . . . . . . . . . . 57
5.4.1 ILS-TS OpenMP . . . . . . . . . . . . . . . . . . . . . . . . 57
5.4.2 ILS-TS-CUDA . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.4.3 ILS-TS-Multikernels . . . . . . . . . . . . . . . . . . . . . 63
5.4.4 ILS-TS-multi GPU . . . . . . . . . . . . . . . . . . . . . . 64
5.4.5 ILS-TS-MPI-cuda . . . . . . . . . . . . . . . . . . . . . . . 64
5.4.6 ILS-TS-MPI-OpenMP-cuda . . . . . . . . . . . . . . . . . 66
5.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

6 Expérimentation 68
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2 Environnement d’exécution . . . . . . . . . . . . . . . . . . . . . 69
6.3 Résultats sur les différentes architectures . . . . . . . . . . . . 70
6.3.1 Résultats d’exécution de la version MPI -OpenMP-
CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
SOMMAIRE v

6.4 Interprétation des résultats . . . . . . . . . . . . . . . . . . . . . 71


6.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

7 Conclusion 75
Bibliographie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Table des figures

2.1 Problème de coloration de graphe . . . . . . . . . . . . . . . . . . . . 9

3.1 Taxonomie des méthodes de résolution. . . . . . . . . . . . . . . . . . 15


3.2 l’optimum global d’un problème. . . . . . . . . . . . . . . . . . . . . 17
3.3 Diagramme d’un algorithme génétique général . . . . . . . . . . . . . 19

4.1 Machine SISD. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31


4.2 Machine SIMD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3 Machine MISD. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4 Machines MIMD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5 Architecture multicoeurs . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.6 Comparaison entre GPU et CPU. . . . . . . . . . . . . . . . . . . . . 40
4.7 Exécution d’un programme CUDA. . . . . . . . . . . . . . . . . . . . 44
4.8 Vue générale du modèle de mémoire CUDA. . . . . . . . . . . . . . . 45
4.9 Organisation d’une grille CUDA. . . . . . . . . . . . . . . . . . . . . 47

5.1 Solution du Q3AP de taille N = 8 . . . . . . . . . . . . . . . . . . . . 53


5.2 Organigramme de la méthode hybridée. . . . . . . . . . . . . . . . . . 56
5.3 Shéma d’ILS-TS-OpenMP. . . . . . . . . . . . . . . . . . . . . . . . . 58
5.4 Organigramme ILS-TS sous CUDA. . . . . . . . . . . . . . . . . . . . 60

6.1 résultats sur l’instance nug8. . . . . . . . . . . . . . . . . . . . . . . . 72


6.2 résultats sur l’instance nug12. . . . . . . . . . . . . . . . . . . . . . . 72
6.3 résultats sur l’instance nug13. . . . . . . . . . . . . . . . . . . . . . . 73
6.4 résultats sur l’instance nug15. . . . . . . . . . . . . . . . . . . . . . . 73
6.5 résultats sur l’instance nug18. . . . . . . . . . . . . . . . . . . . . . . 73
6.6 résultats sur l’instance nug22. . . . . . . . . . . . . . . . . . . . . . . 74

vi
Liste des tableaux

4.1 Taxonomie de Flynn 1966. . . . . . . . . . . . . . . . . . . . . . . . . 31


4.2 Formats des directives OpenMP. . . . . . . . . . . . . . . . . . . . . . 36
4.3 Fonction de base MPI. . . . . . . . . . . . . . . . . . . . . . . . . . . 38

6.1 Caractéristiques des deux configurations. . . . . . . . . . . . . . . . . 69


6.2 Résultat d’exécution avec cuda. . . . . . . . . . . . . . . . . . . . . . 70
6.3 Résultat d’exécution avec OpenMP et cuda. . . . . . . . . . . . . . . 70
6.4 Résultat d’exécution avec MPI-CUDA. . . . . . . . . . . . . . . . . . 71
6.5 Résultat d’exécution avec MPI-OpenMP-cuda. . . . . . . . . . . . . . 71

vii
Liste d’algorithmes

1 Pseudo code de la recherche aléatoire . . . . . . . . . . . . . . . . . . 23


2 Pseudo code d’une recherche locale . . . . . . . . . . . . . . . . . . . 23
3 Pseudo code du recuit simulé . . . . . . . . . . . . . . . . . . . . . . 24
4 Pseudo code d’une recherche tabou . . . . . . . . . . . . . . . . . . . 26
5 Pseudo code d’une ILS . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6 Algorithme d’hybridation ILS-TS . . . . . . . . . . . . . . . . . . . . 52

viii
Listings

4.2 Hello World avec MPI . . . . . . . . . . . . . . . . . . . . . . . . . . 38


4.5 Addition de deux vecteurs . . . . . . . . . . . . . . . . . . . . . . . . 48
5.1 Structure d’une solution . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.2 Création d’une solution . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.6 Pricncipaux étapes ILS–TS–OpenMP . . . . . . . . . . . . . . . . . . 59
5.7 Fonction de génération évaluation de voisinage CUDA . . . . . . . . . 61
5.8 Appel du kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

ix
LISTINGS 1

Résumé

Le problème d’affectation quadratique à 3 dimensions (Q3AP) est un problème


d’optimisation combinatoire (POC) difficile récurrent dans plusieurs domaines de la
vie courante tel que : Le routage dans les réseaux de télécommunication, affectation
de ressources dans un processus industriel la bio-informatique ou encore l’extraction
de connaissances.

Pour résoudre ce type de problème, on fait recours aux méthodes de résolution


des (POC) classifiées en deux grandes classes : Méthodes exactes et Méthodes appro-
chées. On s’intéresse dans nos travaux à la deuxième classe et plus exactement aux
métaheuristiques qui visent a obtenir des solutions de bonne qualité en un temps
raisonnable. On peut citer parmi ces méthodes, la recherche tabou, et les algorithmes
génétiques.

En tenant compte des architectures existantes actuellement, on s’est penché vers


les accélérateurs graphiques (GPU ) qui offrent désormais des atouts indéniables au
sens de la parallélisation massive.

Nous avons réalisé dans ce mémoire maintes versions d’implémentation d’un al-
gorithme de recherche tabou itérative en utilisant une panoplie d’architectures d’exé-
cution pour mettre en évidence l’avantage des GPU par rapport aux architectures
classiques.

Mots clés : Q3AP, QAP, CUDA, GPU , accélérateurs graphiques, recherche


tabou.
LISTINGS 2

Abstract

The 3 dimensional quadratic affectation problem (Q3AP) is a hard combina-


tory optimization problem (COP) occuring in many fields of life such as : Network
routing, ressource affectation in industrial process, bio informatics or knowledge
extraction and so on. This will be our main concern.

To resolve this problem, we refer to COP resolving methods divided into two big
categories : Exact and approximate methods. We will focus our works on the second
class and exactly in metaheuristics which aim to find good solutions in reasonable
time. We can cite tabu search and genetic algorithms.

Considering different architecture, we choose graphic accelerators GPU which


give nowadays important advantages in massive parallelization.

We realized in this thesis many implementations of an iterative tabu search


algorithm over a panoply of execution’s architecture to show the advantage of GPU
over classical architecture.

Keywords : Q3AP, QAP, CUDA, GPU , Graphic accelerators, Tabu search.


Chapitre 1

Introduction générale

«
’est la démarche intellectuelle qui est au cœur de la science du
C xxie siècle. La combinatoire consiste à combiner des éléments
simples en séquences, en architecture, et à construire ainsi des systèmes
complexes. (. . . ) Les sciences de la combinatoire font appel à des for-
malismes particuliers, à des logiques spécifiques, particulières, adaptés.
Voilà la vie de la nouvelle science, par laquelle le développement semble
infini. »
C Allègle Dictionnaire amoureux de la science,2005.

Les ingénieurs se heurtent quotidiennement à des problèmes technologiques de


complexité grandissante, qui surgissent dans des secteurs très divers, comme dans
le traitement des images, la conception de systèmes mécaniques, la recherche opéra-
tionnelle et l’électronique. Le problème à résoudre peut fréquemment être exprimé
sous la forme générale d’un problème d’optimisation, dans lequel on définit une fonc-
tion objectif, ou fonction de coût (voire plusieurs), que l’on cherche à minimiser par
rapport à tous les paramètres concernés. Par exemple, dans le célèbre problème du
voyageur de commerce, on cherche à minimiser la longueur de la tournée d’un "voya-
geur de commerce", qui doit visiter un certain nombre de villes, avant de retourner
à la ville de départ. La définition du problème d’optimisation est souvent complétée
par la donnée de contraintes : tous les paramètres (ou variables de décision) de la
solution proposée doivent respecter ces contraintes, faute de quoi la solution n’est
pas réalisable.

Etant donnée l’importance de ces problèmes, de nombreuses méthodes de ré-


solution ont été développées. Ces méthodes peuvent être classées en deux grandes

3
CHAPITRE 1. INTRODUCTION GÉNÉRALE 4

catégories : les méthodes exactes et les méthodes approchées (approximatives).

Les méthodes exactes ont permis de trouver des solutions optimales pour des
problèmes de taille raisonnable. Malgré les progrès réalisés, comme le temps de calcul
nécessaire pour trouver une solution risque d’augmenter exponentiellement avec la
taille du problème, les méthodes exactes rencontrent généralement des difficultés
face aux applications de taille importante.

Les méthodes approchées constituent une alternative très intéressante pour trai-
ter les problèmes d’optimisation de grande taille si l’optimalité n’est pas primordiale.
En effet, ces méthodes sont utilisées depuis longtemps. On peut les classifier en deux :
les heuristiques (méthodes dédiées à un problème spécifique) et les méta-heuristiques
(plus générales).

Les métaheuristiques sont des méthodes génériques utilisées pour la résolution


approchée de problèmes d’optimisation combinatoire (POC) NP-difficiles. De nom-
breuses métaheuristiques existent et peuvent être scindées en deux classes princi-
pales : les métaheuristiques à solution unique et les métaheuristiques à population de
solutions. Les métaheuristiques de la première classe les plus connues sont le recuit
simulé, la recherche tabou, la méthode ILS (Iterated Local Search), VNS (Variable
Neighborhood Search), etc. Les métaheuristiques les plus connues de la seconde
classe sont les algorithmes génétiques, la méthode GRASP (Greedy Randomized
Adaptive Search Procedure), les colonies de fourmis, etc.

Malgré leurs robustesses, ces méthodes sont soit puissantes en termes d’exploi-
tation du voisinage au détriment de l’intensification, soit le contraire et c’est qui a
poussé les scientifiques a penser à l’hybridation entre ces méthodes pour parer à ce
genre de problème.

Dans ce magister nous nous intéressons à l’implémentation de méthodes de re-


cherches locales pour la résolution du problème d’affectation quadratique à 3 dimen-
sions "Q3AP".

Le "Q3AP" est une extension du problème d’affectation quadratique (QAP), qui


occupe une large place dans la littérature d’Optimisation Combinatoire.

Le Q3AP a été présenté par P. Hahn et al. [14]. La motivation principale a été
l’optimisation d’un schéma de retransmission sur réseau sans fil, en affectant des
symboles de modulation à des séquences binaires de taille fixe. Des erreurs de trans-
mission pouvant avoir lieu, les paquets erronés sont susceptibles d’être retransmis.
La diversité est une mesure de l’indépendance statistique entre une transmission et
les éventuelles retransmissions successives. Maximiser la diversité revient à réduire
4
CHAPITRE 1. INTRODUCTION GÉNÉRALE 5

la probabilité d’erreurs lors des retransmissions. Comme présenté par P. Hahn et al.
, lorsque l’on considère qu’une seule retransmission, ce problème revient à un QAP.
Le Q3AP correspond au cas de deux retransmissions.

Pour résoudre le Q3AP nous avons opté pour une exécution parallèle sur les
cartes graphiques NVidia 1 , celles-ci offrent un environnement adéquat pour ce genre
de situation. Et pour pousser le parallélisme au plus profond, on a opter pour une
implémentation sur architectures multi-cœurs et GPU en même temps.

Ce mémoire est structuré comme suit :

Le premier chapitre cite brièvement les notions abordés et la structure de ce


document.

Le second consiste à définir le problème traité et donner sa formulation mathé-


matique.

Quant au troisième chapitre, nous aborderons différentes méthodes de résolution


utilisées dans le jargon de la combinatoire.

Le quatrième chapitre définira les différentes plateformes utilisées et détaillera la


conception et la mise en œuvre des outils nécessaires à l’implémentation de l’appli-
cation proprement dite.

Le cinquième chapitre décrira les différentes étapes d’expérimentation et les ré-


sultats obtenues.

Finalement on clôturera le document par une conclusion et des perspectives.

1. http://www.nvidia.com le site officiel de Nvidia


5
Chapitre 2

Le problème d’affectation
quadratique à 3 dimension
(Q3AP)

Sommaire
2.1 Les problèmes d’optimisation combinatoire . . . . . . . . 7
2.2 Quelques exemples de problèmes d’optimisation com-
binatoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Le problème d’affectation quadratique à 3 dimensions
Q3AP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.1 Définition du problème QAP . . . . . . . . . . . . . 10
2.3.2 Définition du problème Q3AP . . . . . . . . . . . . 11

6
2.1 Les problèmes d’optimisation combinatoire 7

Dans la vie courante, nous sommes fréquemment confrontés à des problèmes


d’optimisation plus ou moins complexes. Cela peut commencer au moment où l’on
tente de ranger un bureau, de placer un mobilier, et aller jusqu’à un processus
industriel, par exemple pour la planification de différentes tâches.

Dans ce chapitre nous allons définir certains problèmes d’optimisation parmi les
plus connus de la littérature avec quelques concepts de base. Puis on passera a une
définition plus en moins détaillé du problème Q3AP.

2.1 Les problèmes d’optimisation combinatoire

Dans la littérature, divers problèmes d’optimisation reçoivent l’appellation suite


à un domaine étudié, nous consacrons la partie suivante pour décrire quelques uns.
L’optimisation combinatoire regroupe une large classe de problèmes ayant des ap-
plications dans de nombreux domaines applicatifs.

Un problème d’optimisation combinatoire est défini par un ensemble fini de so-


lutions discrètes D et une fonction objectif f associant à chaque solution une valeur
(la plupart du temps, une valeur réelle). Ainsi, un problème d’optimisation com-
binatoire consiste en l’optimisation (minimisation ou maximisation) d’un certain
critère sous différentes contraintes permettant de délimiter l’ensemble des solutions
réalisables (ou solutions admissibles).

La variété des problèmes d’optimisation combinatoire est en particulier dûe


au large spectre de ses applications. En effet, que l’on s’intéresse à l’optimisation
d’un système de production, au design de réseaux de télécommunication, à la bio-
informatique ou encore à l’extraction de connaissances, nous pouvons être confrontés
à des problèmes d’optimisation combinatoire.

Les problèmes d’optimisation apparaissent de plus en plus dans des secteurs


techniques comme le traitement d’images, l’électronique, la médecine, la recherche
opérationnelle ou encore les systèmes mécaniques. On définit ainsi une fonction
objectif que l’on cherche à minimiser (ou à maximiser) par rapport à toutes les
contraintes d’un problème. Parmi les problèmes d’optimisation, on peut parler des
problèmes d’optimisation dits "discret", pour lesquels on ne connaît pas de méthodes
de résolution telle que la complexité en temps soit borné par un polynôme de degré
n. Donc pour illustrer ces propos, on peut notamment citer le problème du voyageur
de commerce ou encore le problème de l’affectation quadratique.

7
2.2 Quelques exemples de problèmes d’optimisation combinatoire 8

2.2 Quelques exemples de problèmes d’optimisa-


tion combinatoire
Le problème VRP Après presque cinquante années de recherche, le problème
d’élaboration de tournées de véhicules introduit par Dantzig et al en 1959
[22] demeure un des nombreux défis les plus liés à la vie courante, ce qui jus-
tifie que de nombreuses équipes de recherche s’y investissent. Communément
appelé VRP (Vehicle Routing Problem), le principe général de ce problème
consiste, en optimisant un ou plusieurs critères, à construire un ensemble de
tournées, pour un nombre fini de véhicules, commençant et finissant à un dé-
pôt. Dans ces tournées, un client doit être desservi une seule fois par un seul
véhicule et la capacité de transport d’un véhicule pour une tournée ne doit pas
être dépassée.
Le VRP peut être un problème de collecte et ou de distribution. Dans tous ces
cas, chaque client est affecté à une tournée assurée par un seul véhicule. Une
solution à ce problème consiste à confectionner l’ensemble des trajets assurant
la visite des clients. Les trajets proposés doivent respecter les différentes ca-
pacités des véhicules et satisfaire les quantités demandées par les clients ou à
livrer à ces derniers. Le critère le plus utilisé dans ce genre de problème est
la minimisation de la distance totale parcourue par un nombre minimum de
véhicules.
Le problème SAT Le problème SAT (Boolean Satisfability Problem) est un pro-
blème NP-complet. Il consiste à trouver un ensemble de variables booléennes
X = (X1 , . . . , Xn ) pour qu’une expression booléenne donnée soit vraie.
En effet, l’expression booléenne doit être une conjonction de clauses dans la-
quelle les clauses sont représentées par des disjonctions de k variables (K-SAT).
De diverses applications utilisent ce problème tel que le routage, la logique,
l’informatique, la gestion d’emploi de temps ...
Définition 2.2.0.1 Un problème SAT est représenté au moyen de n variables
propositionnelles (x1 , x2 , . . . , xn ) qui peuvent prendre la valeur (faux) ou (vrai).
Coloration de graphe Le problème de coloration de graphe est en fait à l’origine
de la théorie des graphes elle-même. Il s’agit de colorier tous les sommets d’un
graphe tel que chaque sommet ait une couleur différente de la couleur de ces
sommets adjacents tout en minimisant le nombre de couleurs.
La coloration de graphe est appliquée dans multiples domaines tels que l’em-
ploi de temps, le problème de feux, la coloration des cartes, l’affectation de
8
2.3 Le problème d’affectation quadratique à 3 dimensions Q3AP 9

fréquence en téléphonie mobile, etc...


Définition 2.2.0.2 Soit le graphe G = (S, A) avec S l’ensemble des sommets
et A l’ensemble des arcs.
La coloration C : s → c telle que si (s1, s2) ∈ A alors C(s1) 6= C(s2).

Figure 2.1 – Problème de coloration de graphe

Voyageur de commerce (TSP) Le problème du Voyageur de Commerce (TSP)


consiste à visiter un ensemble de villes en passant une et une seule fois par cha-
cune d’elles et revenir à la ville de départ tout en minimisant la distance totale
parcourue. Une instance d’un TSP est représentée par un graphe G(N, E) et
une fonction de coût d : E → R où N est l’ensemble des nœuds de G (chaque
nœud est associé à une ville), E ⊆ (N × N ) est l’ensemble des arêtes reliant
les nœuds de N deux à deux et d(i, j) est la distance entre les deux nœuds i
et j. Le coût d’un cycle du graphe G est la somme des coûts des arêtes qui le
composent. Résoudre un TSP consiste à trouver un cycle hamiltonien (passant
par chaque sommet une et une seule fois) de coût minimal.

2.3 Le problème d’affectation quadratique à 3 di-


mensions Q3AP

Le problème d’affectation quadratique à trois dimensions (Q3AP) est une ex-


tension du problème d’affectation quadratique (QAP), qui occupe une large place
dans la littérature d’optimisation combinatoire. C’est l’acronyme anglais "Quadratic
Affectation Problem".

Avant d’introduire sur le Q3AP, nous allons définir le problème QAP.

9
2.3 Le problème d’affectation quadratique à 3 dimensions Q3AP 10

2.3.1 Définition du problème QAP

Le problème de l’affectation quadratique (QAP) peut être formulé comme suit :


étant donnés n unités et n sites où elles peuvent être placées, connaissant le flot fij
circulant entre les unités i et j (i, j = 0, . . . , n − 1) et la distance drs entre les sites r
et s (r, s = 0, . . . , n − 1), on cherche un placement des unités sur les sites minimisant
la somme totale des produits flots × distances. Mathématiquement, on cherche une
permutation π de P (n), l’ensemble des permutations de n éléments, minimisant :

 
n−1
X n−1
X 
min fij dπ(i)π(j) , (2.1)
π∈Πn  
i=0 j=0

Cette formulation est connue sous le nom de Koopmans-Beckmann.


Ce problème a été posé pour la première fois par Koopmans & Beckman [26] ; il
est NP-difficile, même si l’on ne désire trouver qu’une solution approchée à  près.
Beaucoup de problèmes peuvent être modélisés comme un QAP, à commencer par
le problème du voyageur de commerce, la conception de bâtiments, l’équilibrage de
turbines, la génération de trames de gris, etc. Il s’agit sans doute d’un des problèmes
d’optimisation combinatoire les plus difficiles puisque les méthodes exactes actuelles
résolvent avec peine des problèmes de taille n = 20 [37].
Si l’on considère, en plus des flux et des distances entre les sites, le coût cik
d’affectation de l’objet i au site k, le QAP s’écrit sous la forme suivante :

 
n−1
X n−1
X n−1
X 
min fij dπ(i)π(j) + ciπ(i) , (2.2)
π∈Πn  
i=0 j=0 i=0

Soit X = (xij ) la matrice carrée n × n définie comme suit :


n−1
X
xij = 1, i = 0, 1, .., n − 1; (2.3)
j=0

n−1
X
xij = 1, j = 0, 1, .., n − 1; (2.4)
j=0

xij ∈ {0, 1} i, j = 0, 1, .., n − 1. (2.5)


La matrice X ainsi définie est appelée matrice de permutations et le QAP 2.1 peut
être reformulé comme suit :
 
n−1
X n−1
X n−1
X n−1
X 
min fij dkl xik xjl (2.6)
 
i=0 j=0 k=0 l=0

10
2.3 Le problème d’affectation quadratique à 3 dimensions Q3AP 11

t.q. :
n−1
X
xij = 1, i = 0, 1, .., n − 1; (2.7)
j=0

n−1
X
xij = 1, j = 0, 1, .., n − 1; (2.8)
j=0

xij ∈ {0, 1} i, j = 0, 1, .., n − 1. (2.9)


La formulation du QAP par les expressions 2.6-2.9 est appelée formulation quadra-
tique en 0-1.

2.3.2 Définition du problème Q3AP

Le problème d’affectation quadratique à 3 dimensions est une généralisation du


problème QAP décrit dans la section précédente. Le Q3AP a été introduit par
William P. Pierskalla en 1967 [33] et revisité par Samra et al. [35] et Hahn et al.
[15]. La motivation principale a été l’optimisation d’un schéma de retransmission sur
réseau sans fil, en affectant des symboles de modulation à des séquences binaires de
taille fixe. Des erreurs de transmission pouvant avoir lieu, les paquets erronés sont
susceptibles d’être retransmis. La diversité est une mesure de l’indépendance statis-
tique entre une transmission et les éventuelles retransmission successives. Maximiser
la diversité revient à réduire la probabilité d’erreurs lors des retransmissions. Comme
présenté par P. Hahn et al., lorsque l’on considère qu’une seule retransmission, ce
problème revient à un QAP. Le Q3AP correspond au cas de deux retransmissions.
Une autre illustration de ce problème peut être faite en reprenant les termes
du QAP. Etant donnés N managers, N usines, et N sites, le problème consiste à
affecter chaque manager et chaque usine à chaque site, de telle sorte à minimiser le
coût total. Le coût d’une affectation est la somme de coûts linéaires Lijk où i est le
manager, j l’usine et k le site, plus les coûts quadratiques Cijklmn où i et l sont des
managers, j et m des usines et k et m des sites. Plus formellement le problème se
modélise comme suit :
 
n−1
X n−1
X n−1
X n−1
X n−1
X n−1
X n−1
X n−1
X n−1
X 
min  bijp uij wip + cijpkmq uij ukm wip wkq  , (2.10)
i=0 j=0 p=0 i=0 j=0 p=0 k=0 m=0 q=0


 
 n−1
X n−1
X 
u, w ∈ x≥0: xij = 1 pour j = 0, .., n − 1; xij = 1 pour i = 0, .., n − 1; .
 
i=0 j=0
(2.11)
11
2.3 Le problème d’affectation quadratique à 3 dimensions Q3AP 12

Si l’on pose uij wip = xijp et ukm wkq = xkmq , on obtiendrait la formulation équivalente
à 2.10 suivante :
 
n−1
X n−1
X n−1 n−1
X n−1
X n−1
X n−1
X n−1
X n−1

 

X X 
min bijp xijp + cijpkmq xijp xkmq  , (2.12)

 i=0 j=0 p=0
 i=0 j=0 p=0 k=0 m=0 q=0 

k6=i m6=j q6=p

Où :
X = (xijl ) ∈ I ∩ J ∩ L, (2.13)

xijl = 1 , i, j, l = 0, .., n − 1. (2.14)

Les ensembles I, J et L sont définis comme suit :

n−1
X n−1
X
I = {X = (xijl ) : xijl = 1, i = 0, 1, .., n − 1}; (2.15)
j=0 l=0

n−1
X n−1
X
J = {X = (xijl ) : xijl = 1, j = 0, 1, .., n − 1}; (2.16)
i=0 l=0

X n−1
n−1 X
L = {X = (xijl ) : xijl = 1, l = 0, 1, .., n − 1}; (2.17)
i=0 j=0

Pour le QAP, le problème consiste à trouver la matrice de permutations de di-


mension 2 qui maximise la fonction quadratique. Pour le Q3AP, le problème consiste
à minimiser la fonction quadratique sur un polytope d’affectation à 3 dimensions
I×J×L. C’est la raison pour laquelle ce problème est ainsi dénommé.

Une autre formulation du Q3AP particulièrement utilisée pour l’implémentation


des métaheuristiques est la formulation à base de permutations dans laquelle le
Q3AP défini par les équations 2.12-2.14 peut être formulé comme suit :
 
n−1
X n−1
X n−1
X 
min Ciφ(i)φ(j)jϕ(j)ϕ()i + biφ(i)ϕ(i) . (2.18)
φ,ϕ  
i=0 j=0 i=0

Où φ et ϕ sont des permutations sur l’ensemble {0, 1, .., n − 1}.

Par rapport à cette formulation, minimiser le Q3AP revient à trouver la double-


permutation (φ, ϕ) qui minimise l’expression 2.18.

12
Chapitre 3

Méthodes de résolution des


problèmes combinatoires

Sommaire
3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Méthodes de résolution . . . . . . . . . . . . . . . . . . . . . 14
3.3 Méthodes exactes . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 Méthode "Branch and Bound" (B & B) . . . . . . 15
3.3.2 Programmation dynamique . . . . . . . . . . . . . . 16
3.4 Les métaheuristiques . . . . . . . . . . . . . . . . . . . . . . . 16
3.4.1 Définitions . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.4.2 Métaheuristique à population de solution . . . . . 18
3.4.2.1 Les algorithmes évolutionnistes . . . . . . . . 18
3.4.2.2 Algorithmes génétiques . . . . . . . . . . . . . 18
3.4.2.3 Les colonies de fourmis . . . . . . . . . . . . . 20
3.4.2.4 Programmation évolutive . . . . . . . . . . . . 21
3.4.2.5 Stratégies d’évolution "SE" . . . . . . . . . . . 22
3.4.3 Métaheuristique à solution unique . . . . . . . . . 22
3.4.3.1 Recherche aléatoire (RS) . . . . . . . . . . . . 22
3.4.3.2 Recherche Locale (LS) . . . . . . . . . . . . . 22
3.4.3.3 Recuit Simulé (SA) . . . . . . . . . . . . . . . 23
3.4.3.4 Recherche Tabou TS . . . . . . . . . . . . . . 24
3.4.3.5 Recherche Locale Itérée (Iterated Local Search) 25
3.5 Métaheuristiques hybrides . . . . . . . . . . . . . . . . . . . 27
3.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

13
3.1 Introduction 14

3.1 Introduction

Après avoir vu les différents problèmes d’optimisation combinatoire, nous allons


maintenant survoler les différentes méthodes de résolution de ces derniers.

Il existe un très grand nombre de méthodes de résolutions de ces problèmes class-


sées en deux grandes catégories : les méthodes exactes et les méthodes approchées.

Parmi les méthodes approchées, on distingue les heuristiques et les métaheuris-


tiques. dans ce mémoire on s’est intéressé aux métaheuristiques.

Ce chapitre est organisé comme suit : La section 3.3 cite quelques méthodes
exactes les plus connus dans la littérature.

La section 3.4 donne des définitions des méthodes métaheursitiques. Quant à la


section 3.4.2, elle définit quelques métaheuristiques les plus utilisées.

On parlera d’une hybridation de ces méthodes dans la section 3.5 puis on clôtu-
rera le chapitre par une conclusion.

3.2 Méthodes de résolution

Un très grand nombre de méthodes de résolution existent dans la littérature pour


l’optimisation combinatoire. La figure 3.2 met en parallèle les différentes méthodes
d’optimisation.

Ces méthodes font partie de deux groupes de nature différente :

– Le premier groupe, comprend les méthodes exactes d’arborescence qui garan-


tissent la complétude de la résolution : c’est le cas de A*, Branch and X et
programmation dynamique. Le temps de calcul nécessaire d’une telle méthode
augmente en général exponentiellement avec la taille du problème à résoudre
(dans le pire des cas).
– Le second groupe, comprend les méthodes approchées dont le but est de trouver
une solution de bonne qualité en un temps de calcul raisonnable sans garantir
l’optimalité de la solution obtenue. Dans ce cas, les méthodes approchées sont
fondées principalement sur diverses heuristiques, souvent spécifiques à un type
de problème.

Les métaheuristiques constituent une autre partie importante des méthodes ap-
prochées et ouvrent des voies très intéressantes en matière de conception de méthodes
heuristiques pour l’optimisation combinatoire.

14
3.3 Méthodes exactes 15

Méthodes de
résolution

Méthodes Méthodes
Exactes Approchées

B& Programmation
A∗
X dynamique

Métaheuristiques Heuristiques

Solution Population
unique de solution

Recuit Recherche Colonies Recherche


Descente AE
simulé tabou de fourmis dispersée

Figure 3.1 – Taxonomie des méthodes de résolution.

3.3 Méthodes exactes

Les méthodes exactes reposent sur l’utilisation d’algorithmes qui mènent de façon
sûre vers la solution optimale. Le principe essentiel de ces méthodes est d’énumé-
rer de manière implicite l’ensemble des solutions de l’espace de recherche. Malgré
l’important temps de calcul que nécessitent, généralement, ces approches, plusieurs
méthodes ont été développées.

3.3.1 Méthode "Branch and Bound" (B & B)

Un algorithme par séparation et évaluation est une méthode générique de réso-


lution de problèmes d’optimisation, et plus particulièrement d’optimisation combi-
natoire ou discrète. C’est une méthode d’énumération implicite : toutes les solutions
possibles du problème peuvent être énumérées mais, l’analyse des propriétés du pro-
blème permet d’éviter l’énumération de larges classes de mauvaises solutions. Dans
un bon algorithme par séparation et évaluation, seules les solutions potentiellement
bonnes sont donc énumérées [?].

15
3.4 Les métaheuristiques 16

Le principe de la méthode de séparation & évaluation B & B est de découper


l’espace de recherche en domaines plus restreints afin de séparer l’optimum global.
l’algorithme de recherche forme donc un arbre dont chaque nœud représente une
partie de l’espace. Ensuite chaque nœud est évalué de façon à déterminer sa borne
(bound) inférieure 1 en fonction d’un critère d’évaluation. Si cette borne n’est pas
meilleure que la solution courante alors la recherche est stoppée, sinon la séparation
continue.

3.3.2 Programmation dynamique

La programmation dynamique est une technique algorithmique pour optimiser


des sommes de fonctions monotones croissantes sous contrainte.

Elle a été désignée par ce terme pour la première fois dans les années 1940 par
Richard Bellman. Elle s’applique à des problèmes d’optimisation dont la fonction
objectif se décrit comme « la somme de fonctions monotones croissantes des res-
sources ».

Elle a d’emblée connu un grand succès, car la plupart des fonctions économiques
de l’industrie étaient de ce type : maximisation du tonnage de charbon (ou de barils
de pétrole) produit à partir de plusieurs puits à budget donné, par exemple.

La programmation dynamique s’appuie sur un principe simple : toute solution


optimale s’appuie elle-même sur des sous-problèmes résolus localement de façon
optimale. Concrètement, cela signifie que l’on va pouvoir déduire la solution optimale
d’un problème en combinant des solutions optimales d’une série de sous problèmes.
Les solutions des problèmes sont étudiées ’de bas en haut’, c’est - à - dire qu’on
calcule les solutions des sous-problèmes les plus petits pour ensuite déduire petit à
petit les solutions de l’ensemble.

3.4 Les métaheuristiques

Les méthodes d’optimisation sont des algorithmes évaluant des solutions asso-
ciées à des problèmes bien déterminés soit de manière complète ou partielle.

Parmi les différentes méthodes d’optimisation, nous allons uniquement nous in-
téresser aux métaheuristiques.
1. Inférieure dans le cas d’une minimisation, Supérieure dans le cas d’une maximisation.

16
3.4 Les métaheuristiques 17

Le but des métaheuristiques est similaire à celui des heuristiques : obtenir des
solutions de bonne qualité en un temps raisonnable. Cependant, contrairement à
une heuristique, l’algorithme et l’emploi d’une métaheuristique est totalement indé-
pendants du problème à traiter.

Les métaheuristiques sont subdivisées en deux grandes familles :


1. les métaheuristiques de recherche locale :
– la méthode de la descente ;
– la recherche tabou.
2. les métaheuristiques d’évolution :
– les algorithmes génétiques ;
– la recherche distribuée ;
– les colonies de fourmis.

3.4.1 Définitions

Les métaheuristiques sont apparues au début des années 1980 avec une ambition
commune : résoudre au mieux les problèmes dits d’optimisation difficile. En effet,
celles-ci s’appliquent à toutes sortes de problèmes discrets, et elles peuvent s’adapter
aussi aux problèmes continus.

Les métaheuristiques sont généralement des algorithmes stochastiques itératifs,


qui progressent vers un optimum global 3.4.1, c’est-à-dire l’extremum global d’une
fonction, par échantillonnage d’une fonction objectif.

Figure 3.2 – l’optimum global d’un problème.

N (x) = y ∈ S : dist(x, y) ≤ 

17
3.4 Les métaheuristiques 18

Il existe un grand nombre de métaheuristiques différentes, allant des métaheu-


ristiques à solution unique à des métaheuristiques à base de population.

3.4.2 Métaheuristique à population de solution

3.4.2.1 Les algorithmes évolutionnistes

Les algorithmes évolutionnistes ou algorithmes évolutionnaires (evolutionary com-


putation en anglais), sont une famille d’algorithmes s’inspirant de la théorie de
l’évolution pour résoudre des problèmes divers. Ils font ainsi évoluer un ensemble
de solutions à un problème donné, dans l’optique de trouver les meilleurs résultats.
Ce sont des algorithmes stochastiques, car ils utilisent itérativement des processus
aléatoires.
Historiquement, trois grandes familles d’algorithmes ont été développées indé-
pendamment, entre la moitié des années 1960 et 70. Les premières méthodes furent
les stratégies d’évolution, proposées par I. Rechenberg en 1965 [34], pour résoudre
des problèmes d’optimisations continus. L’année suivante, Fogel, Owens et Walsh
conçoivent la programmation évolutionnaire [11] comme une méthode d’intelligence
artificielle pour la conception d’automates à états finis. Enfin, en 1975, J. H. Holland
propose les premiers algorithmes génétiques [17], pour l’optimisation combinatoire.
La parution en 1989 du livre de D. E. Goldberg sur les algorithmes génétiques [13]
rendra ceux-ci particulièrements populaires.
Par la suite, ces différentes approches ont beaucoup évolué et se sont rapprochées,
pour finir par êtres regroupées sous le terme générique d’algorithmes évolutionnaires.
Aujourd’hui, la littérature sur le sujet est extrêmement abondante, et ces algorithmes
sont considérés comme un domaine de recherche très prolifique.

3.4.2.2 Algorithmes génétiques

Dans les algorithmes génétiques, nous essayons de simuler le processus d’évolu-


tion d’une population. Nous partons d’une population de N solutions du problème
représentées par des individus. Cette population choisie aléatoirement est appelée
population parent. Le degré d’adaptation d’un individu à l’environnement est ex-
primé par la valeur de la fonction coût f (x), où x est la solution que l’individu
représente. On dit qu’un individu est d’autant mieux adapté à son environnement,
si le coût de la solution qu’il représente est plus faible. Au sein de cette popula-
tion, intervient alors plusieurs méthodes de sélection d’un ou de deux parents, qui
18
3.4 Les métaheuristiques 19

produisent une nouvelle solution, à travers les opérateurs génétiques, tels que le croi-
sement et la mutation. La nouvelle population, obtenue par le choix de N individus
parmi les populations parent et enfant, est appelée génération suivante [13].

En itérant ce processus, on produit une population plus riche en individus mieux


adaptés. Le processus de l’algorithme génétique est présenté dans la figure 3.3. Il

Figure 3.3 – Diagramme d’un algorithme génétique général

existe plusieurs techniques de sélection. Voici les principales utilisées :


Sélection par rang Cette technique de sélection choisit toujours les individus pos-
sédant les meilleurs scores d’adaptation, le hasard n’entre donc pas dans ce
mode de sélection. En fait, si n individus constituent la population, la sélection
appliquée consiste à conserver les k meilleurs individus (au sens de la fitness)
suivant une probabilité qui dépend du rang (et pas de la fitness).
Roulette Wheel Selection (Appelé aussi roulette ou roue de la fortune)
19
3.4 Les métaheuristiques 20

Pour chaque individu, la probabilité d’être sélectionné est proportionnelle à son


adaptation au problème. Afin de sélectionner un individu, on utilise le principe
de la roue de la fortune biaisée. Cette roue est une roue de la fortune classique
sur laquelle chaque individu est représenté par une portion proportionnelle à
son adaptation. On effectue ensuite un tirage au sort homogène sur cette roue.
Sélection par tournoi Cette technique utilise la sélection proportionnelle sur des
paires d’individus, puis choisit parmi ces paires l’individu qui a le meilleur
score d’adaptation.
Sélection uniforme La sélection se fait aléatoirement, uniformément et sans inter-
vention de la valeur d’adaptation. Chaque individu a donc une probabilité 1/P
d’être sélectionné, où P est le nombre total d’individus dans la population.

3.4.2.3 Les colonies de fourmis

Initialement proposé par Marco Dorigo] et al [6]. dans les années 1990, pour
la recherche de chemins optimaux dans un graphe, le premier algorithme s’inspire du
comportement des fourmis recherchant un chemin entre leur colonie et une source de
nourriture. L’idée originale s’est depuis diversifiée pour résoudre une classe plus large
de problèmes et plusieurs algorithmes ont vu le jour, s’inspirant de divers aspects
du comportement des fourmis.

L’idée originale provient de l’observation de l’exploitation des ressources ali-


mentaires chez les fourmis. En effet, celles-ci, bien qu’ayant individuellement des
capacités cognitives limitées, sont capables collectivement de trouver le chemin le
plus court entre une source de nourriture et leur nid.

Des biologistes ont ainsi observé, dans une série d’expériences menées à partir de
1989, qu’une colonie de fourmis ayant le choix entre deux chemins d’inégale longueur
menant à une source de nourriture, avait tendance à utiliser le chemin le plus court.
Un modèle expliquant ce comportement est le suivant :
1. Une fourmi (appelée « éclaireuse ») parcourt plus ou moins au hasard l’envi-
ronnement autour de la colonie ;
2. si celle-ci découvre une source de nourriture, elle rentre plus ou moins direc-
tement au nid, en laissant sur son chemin une piste de phéromones ;
3. ces phéromones étant attractives, les fourmis passant à proximité vont avoir
tendance à suivre, de façon plus ou moins directe, cette piste ;
4. en revenant au nid, ces mêmes fourmis vont renforcer la piste ;

20
3.4 Les métaheuristiques 21

5. si deux pistes sont possibles pour atteindre la même source de nourriture, celle
étant la plus courte sera, dans le même temps, parcourue par plus de fourmis
que la longue piste ;
6. la piste courte sera donc de plus en plus renforcée, et donc de plus en plus
attractive ;
7. la longue piste, elle, finira par disparaître, les phéromones étant volatiles ;
8. à terme, l’ensemble des fourmis a donc déterminé et " choisi " la piste la plus
courte.

3.4.2.4 Programmation évolutive

La programmation évolutive s’appuie sur un codage approprié du problème à


résoudre et sur les opérations de mutation adaptées au codage [11, 10]. Le codage
d’un tel algorithme dépend du problème à résoudre. Par exemple, pour un problème
d’optimisation dans le domaine des réels, les individus d’une population seraient des
vecteurs de réels. De manière similaire, une permutation serait utilisée pour repré-
senter un tour dans le problème du voyageur de commerce. A partir d’un codage
donné pour un problème, la mutation ou opérateur d’évolution spécifique sera défi-
nie. Par exemple, dans le cas du problème du voyageur de commerce, la mutation
basée sur le codage de permutation pourrait être l’opération d’inversion : prendre
deux villes dans le tour et inverser l’ordre du segment défini par les deux villes. Ainsi
l’analogie est forte avec les méthodes de voisinage : une mutation correspond à un
mouvement dans un algorithme de voisinage.
Un cycle d’évolution typique pour la programmation évolutive est le suivant :
chaque configuration de la population courante est copiée dans une nouvelle popu-
lation. Les configurations sont ensuite mutées, conduisant à de nouvelles configu-
rations. L’ensemble des configurations entre ensuite dans une étape de compétition
pour survivre dans la génération suivante.
La programmation évolutive a été initialement introduite pour simuler l’intel-
ligence qui est définie sur l’hypothèse suivante : la caractéristique principale de
l’intelligence est la capacité d’adaptation comportementale d’un organisme à son
environnement [11]. Selon un modèle très simpliste, cette tâche de simulation re-
vient à prédire une séquence de symboles appartenant à un alphabet fini à partir
des séquences déjà observées. Dans ce but, des automates d’états finis sont choisis
pour représenter les individus d’une population. Ainsi, à chaque automate de la po-
pulation est donnée une série de symboles pris dans une séquence déjà observée et la
21
3.4 Les métaheuristiques 22

sortie de l’automate est mesurée par rapport au résultat déjà connu. Cette mesure
constitue l’adaptation de l’individu. L’étape suivante consiste à créer, pour chaque
individu, un individu enfant par une mutation aléatoire de l’individu parent. La
mutation est assurée par une des 5 opérations suivantes : changer le symbole d’une
sortie, changer un état de transition, ajouter ou supprimer un état et changer l’état
initial.

Aujourd’hui, la programmation évolutive s’est adaptée à l’optimisation combi-


natoire et a produit des résultats intéressants pour certains problèmes [10].

3.4.2.5 Stratégies d’évolution "SE"

Les stratégies d’évolution inventés par Rechenberg 1973, sont conçues dès le dé-
part pour résoudre des problèmes d’optimisation continus. Dans un algorithme SE,
les individus sont des points (vecteurs de réels). Comme la programmation évolutive,
les SE n’utilisent que la mutation et la sélection.

L’algorithme le plus simple, noté (1+1)-ES, manipule un seul individu. A chaque


génération (itération), l’algorithme génère par mutation un individu enfant à partir
de l’individu parent et sélectionne l’un ou l’autre pour le conserver dans la population
(selon l’adaptation de chaque individu). Le processus s’arrête quand la condition
d’arrêt soit satisfaite.

3.4.3 Métaheuristique à solution unique

3.4.3.1 Recherche aléatoire (RS)

C’est la plus simple des méthodes stochastiques. Cette méthode consiste à tirer
à chaque itération une solution au hasard [39]. La fonction objectif f est évaluée en
ce point. La nouvelle valeur est comparée à la précédente. Si elle est meilleure que
la précédente, cette valeur est enregistrée, ainsi que la solution correspondante, et le
processus continu. Sinon on repart du point précédent et on recommence le procédé,
jusqu’à ce que les conditions d’arrêt soient atteintes. L’algorithme 1 est présenté
ci-dessous dans le cas d’un problème de minimisation.

3.4.3.2 Recherche Locale (LS)

La recherche locale "Local Search" est la plus ancienne des méthodes de réso-
lution. Elle démarre avec une solution initiale, et à chaque itération elle remplace
22
3.4 Les métaheuristiques 23

Algorithm 1 Pseudo code de la recherche aléatoire


1: s0 :=solution aléatoire ;
2: f min := f (S0 );
3: xmin := S0 ;
4: répéter
5: S := solution aléatoire ;
6: si (f (S) < f min) alors
7: f min := f (S);
8: xmin := S;
9: fin si
10: jusqu’à condition d’arrêt satisfaite.

la solution courante avec le voisin qui optimise la fonction objective. la recherche


se termine quand tous les voisins candidtas ne sont pas optimaux par rapport à la
solution courante, donc l’optimum local est atteint.
LS est peut être vue comme une descente dans le graphe représentant l’espace
de recherche.
Algorithm 2 Pseudo code d’une recherche locale
1: s = s0 ; // Générer une solution initiale s0
2: tant que Non condition d’arrêt faire
3: Générer N (s) ;// Génération de voisinage
4: si Pas de meilleur voisins alors
5: s = s0 ;// Sélection du meilleur voisin s0 ∈ N (s)
6: fin si
7: fin tant que

3.4.3.3 Recuit Simulé (SA)

Cette méthode est issue d’une analogie entre le phénomène physique de refroi-
dissement lent d’un corps en fusion, qui le conduit à un état solide, de basse énergie.
Il faut abaisser lentement la température, en marquant des paliers suffisamment
longs pour que le corps atteigne l’équilibre thermodynamique à chaque palier de
température [24]. Pour les matériaux, cette basse énergie se manifeste par l’obtention
d’une structure régulière, comme dans les cristaux et l’acier.
L’analogie exploitée par le recuit simulé consiste à considérer une fonction f à
minimiser comme fonction d’énergie, et une solution s peut être considérée comme
23
3.4 Les métaheuristiques 24

un état donné de la matière dont f (s) est l’énergie. Le recuit simulé se démarre
par une solution initiale s qui peut être prise d’une manière aléatoire dans l’espace
de recherche. A cette solution correspond une énergie initiale f (s). Une tempéra-
ture initiale T élevée est également choisis. A chaque itération de l’algorithme une
modification de la solution est effectuée. Cette modification entraîne une variation
∆ de l’énergie du système. Si cette variation est négative (c’est-à-dire qu’elle fait
baisser l’énergie du système), elle est appliquée à la solution courante. Sinon, elle est
acceptée avec une probabilité e∆/T . Ce choix de l’exponentielle pour la probabilité
s’appelle règle de Metropolis. On itère ensuite selon ce procédé en gardant la tempé-
rature constante. L’algorithme Recuit Simulé est présenté ci-dessous (algorithme 3).

Algorithm 3 Pseudo code du recuit simulé


1: Choisir solution initiale s ∈ S et température initiale T ;
2: répéter
3: Choisir aléatoirement s0 ∈ V (s), ∆ = f (s0 ) − f (s) ;
4: si (∆ < 0) alors
5: s := s0 ;
6: sinon
7: Choisir u un nombre aléatoire de [0, 1] ;
8: si (u < e∆/T ) alors
9: s := s0
10: fin si
11: fin si
12: jusqu’à condition d’arrêt satisfaite.

3.4.3.4 Recherche Tabou TS

L’algorithme de la recherche tabou a été proposé par Glover [12] en 1986. Dans
les années 1990, elle est devenu très populaire dans la résolution de problèmes d’op-
timisation d’une manière approximative.
Aujourd’hui, elle est l’une des plus répandus métaheuristiques à solution unique.
L’utilisation de la mémoire, qui stocke les informations relatives au processus de
recherche, représente la caractéristique la plus particulière de cette recherche.
"TS" agit comme un algorithme de recherche locale mais accepte des solutions non
améliorantes pour échapper des optimums locaux quand tous les solutions voisines
ne sont pas améliorantes.
24
3.4 Les métaheuristiques 25

Habituellement tous les voisins sont exploités de manière déterministe. Comme


dans une recherche locale, le meilleur voisin remplace la solution courante. Quand
on est confronté à un optimum local, la recherche continue en choisissant le candidat
le moins mauvais que la solution courante. Le meilleur voisin est considéré comme
nouvelle solution même s’il n’améliore pas la solution courante. Cette politique peut
générer des cycles ; donc des solutions ultérieurement visitées peuvent être revisitées.
Pour éviter ça, la recherche tabou écarte les solutions déjà visitées.

TS gère une mémoire de solutions récemment visitées appelées Liste tabou. Cette
liste constitue la mémoire à court terme de la méthode, elle est mise à jour à chaque
itération. Sauvegardant tous les mouvements est couteux en temps et en espace
mémoire. En effet, on vérifie à chaque itération si une solution appartient ou non
à l’ensemble des solutions visitées. La liste tabou contient un nombre constant de
mouvements tabous, au fait c’est les attributs de ces mouvements qui sont sauve-
gardés.

En introduisant le concept tabou, on peut perdre de l’information à propos de


la recherche. On peut rejeter des solutions qui ne sont pas encore générées. Si un
mouvement est bon, et qu’il est tabou est-ce qu’on le rejette toujours ? Une solution
tabou peut être acceptée si elle répond à des critères appelées "critères d’aspirations".

Quelques mécanismes avancés sont introduites pour traiter les intensifications et


les diversifications.
Intensification (mémoire à moyen terme) Cette mémoire sauvegarde les élites
(meilleurs) solutions retrouvées pour donner la priorité aux attributs de l’en-
semble de ces solutions avec une certaine probabilité.
Diversification (mémoire à long terme) Elle enregistre les informations des so-
lutions visitées tout au long de la recherche. Elle explore les endroits non visi-
tés dans l’espace de recherche. Elle décourage les attributs des solutions élites
parmi les solutions générées pour diversifier la recherche.
L’algorithme 4 suivant décrit cette méthode.

3.4.3.5 Recherche Locale Itérée (Iterated Local Search)

La qualité d’un optimum local obtenu par une recherche locale dépend de la
solution initiale choisie.

Dans une recherche locale multistart, la solution initiale est choisie aléatoirement
et est donc sans rapport avec l’optimum local. ILS améliore les recherches locales

25
3.4 Les métaheuristiques 26

Algorithm 4 Pseudo code d’une recherche tabou


1: s = s0 ; ;// Solution initiale
2: Initialiser la liste tabou, la mémoire à long et à moyen terme ;
3: répéter
4: Trouver le meilleur voisin s0 ; // non tabou ou respecte les critères d’aspiration
5: s = s0 ;
6: Mettre à jour la liste tabou, les critères d’aspiration et les mémoires à long et
à moyen terme ;
7: si Critères d’intensification alors
8: Intensification ;
9: fin si
10: si Critères de diversification alors
11: Diversification ;
12: fin si
13: jusqu’à condition d’arrêt satisfaite.

multistart classiques en perturbant les optimums locaux et en les considérant comme


solutions initiales.
Initialement une recherche locale est appliquée sur une solution initiale, puis à
chaque itération, une perturbation est appliquée sur l’optimum local obtenu. En-
suite une recherche locale est lancée sur la solution perturbée. La solution générée
est acceptée sous quelques conditions. Le processus se répète jusqu’à ce qu’on atteint
des critères d’arrêts. L’algorithme 5 décrit ce processus.

Algorithm 5 Pseudo code d’une ILS


1: s∗ = local search(s0 ); // Appliquer une recherche locale
2: répéter
3: s0 = P erturb(s∗ ) ; // perturber l’optimum local
4: s0∗ = local search(s0 ); // Recherche locale sur la solution perturbée
5: s∗ = Accept(s∗ , s0∗ ); // Selon des critères d’arrêts
6: jusqu’à condition d’arrêt satisfaite.

Les trois éléments qui constituent une ILS :

– Recherche locale : qui peut être une quelconque métahauristique à solution


unique tel qu’une recherche tabou, ou un recuit simulé.
– Méthode de perturbation : c’est un opérateur aléatoire qui fait un mouve-
ment sur la solution courante pour explorer d’autre domaine dans l’espace de
26
3.5 Métaheuristiques hybrides 27

recherche.
– Critère d’acceptation : des conditions sur lesquels on accepte ou non la
solution générée.

3.5 Métaheuristiques hybrides

L’hybridation est une tendance observée dans de nombreux travaux réalisés sur
les métaheuristiques ces dernières années. Elle permet de tirer profit des avantages
cumulés des différentes métaheuristiques, à tel point que les métaheuristiques que
nous avons vues jusqu’à présent ne sont plus que des canevas, des points de départ,
pour commencer à résoudre un problème d’optimisation.

Une des techniques les plus populaires d’hybridation concerne l’utilisation de


métaheuristique de type à " solution unique " avec des métaheuristiques à " popula-
tion de solution " . La plupart des applications réussies d’algorithmes génétiques, ou
à colonies de fourmis, sont complétées par une phase de recherche locale, car c’est
ce qui leur manque à l’origine. Les individus, les fourmis, tentent d’identifier les
zones prometteuses de l’espace des solutions, lesquelles sont ensuite explorées plus
en détail par des méthodes de recherche locale (par l’une des métaheuristiques que
nous avons déjà vues, ou par une heuristique classique, comme celle du plus proche
voisin, ou par un algorithme de type 2-opt).

Ces deux types de recherche, globale et locale, peuvent d’ailleurs s’exécuter de


façon asynchrone, sur des processeurs différents. Le problème d’allocation d’espace a
fait l’objet d’une étude combinant une métaheuristique de type évolutionnaire, avec
une recherche locale à voisinage variable. Ce problème consiste à répartir un certain
nombre de personnes et de ressources matérielles dans un certain nombre de pièces
contient généralement de nombreuses contraintes : nécessité que des personnes se
retrouvent ensemble dans une même pièce, que certaines personnes disposent de
certaines ressources avec elles dans la pièce, etc. Le voisinage variable permet, au
travers de quelques opérations élémentaires (allocation, désallocation, réallocation,
échange,. . . ) de fournir des solutions à la fois faisables (au regard du nombre de
contraintes) et variées.

Une autre manière d’hybrider consiste à exécuter en parallèle plusieurs fois la


même métaheuristique, mais avec des paramètres différents. Ces processus parallèles
communiquent entre eux régulièrement pour échanger de l’information sur leurs
résultats partiels. Cette technique est d’autant plus utilisée que s’est approfondie la

27
3.6 Conclusion 28

recherche sur les architectures parallèles. Toutes les métaheuristiques classiques ont
fait l’objet d’une implémentation parallèle, y compris l’algorithme du recuit simulé
qui, bien que de nature séquentielle, a pu être parallélisé en le divisant en processus
élémentaires.

Enfin une troisième forme d’hybridation combine les métaheuristiques avec des
méthodes exactes. Une méthode exacte peut ainsi donner lieu à une technique effi-
cace pour la détermination du meilleur voisin d’une solution (ce qui peut s’avérer
plus judicieux que de choisir la meilleure solution parmi un petit échantillon de voi-
sins). Dans le domaine de la programmation par contraintes, certaines techniques
de retour-arrière non déterministes (backtracking) ont été associées à des métaheu-
ristiques.

3.6 Conclusion

Les méthodes exactes donnent les meilleurs résultats qui soit en terme de qualité
de solution pour les problèmes d’optimisation combinatoire au détriment des temps
d’exécution.

Face à un problème de taille importante, on est obligé de passé par les heuris-
tiques (pour un problème spécifique) ou les métaheurostiques (plus généralement)
pour gagner du temps d’exécution et parfois de combiner les deux approches (exactes
et heuristiques) ou deux métaheuristiques pour tirer profit des avantages des deux.

Cette hybridation devient une tendance dans la résolution des problèmes d’op-
timisation combinatoire ces dernières années.

28
Chapitre 4

Les architectures multi-cœurs et


les accélérateurs graphiques GPU

Sommaire
4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.2 Les architectures parallèles et multi-cœurs . . . . . . . . . 30
4.2.1 Classification des architectures . . . . . . . . . . . . 30
4.2.2 Architectures multi-cœurs . . . . . . . . . . . . . . . 33
4.3 Modèle de programmation parallèle . . . . . . . . . . . . . 33
4.3.1 OpenMP . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.3.2 Message Passing Interface (MPI ) . . . . . . . . . . 36
4.4 Les accélérateurs Graphiques GPU s . . . . . . . . . . . . . 38
4.4.1 Le GPGPU . . . . . . . . . . . . . . . . . . . . . . . . 39
4.5 BrookGPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.6 OpenCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.7 CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.7.1 Contexte . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.7.2 Modèle de programmation CUDA . . . . . . . . . . 43
4.7.3 Parallélisme de donnée . . . . . . . . . . . . . . . . . 43
4.7.4 Structure d’un programme CUDA . . . . . . . . . 44
4.7.5 Mémoires périphériques et transfert de données 45
4.7.6 Les kernels et les threads . . . . . . . . . . . . . . . 46
4.7.7 Exemple d’un programme CUDA . . . . . . . . . . 48
4.8 conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

29
4.1 Introduction 30

4.1 Introduction

Le monde de l’informatique est un monde très dynamique dans le sens où, nous
rencontrons tous les jours un nombre de technologies récentes. Face a cette avancée
technologique, les concepteurs de logiciels sont obligés à fournir un effort pour s’ali-
gner avec les outils disponibles. De ce fait les architectures parallèles deviennent de
plus en plus accessible au grand public.

Ce chapitre essaye d’introduire sur les différentes architectures parallèles dans


la section 4.2 puis des modèles de programmation sur ces architectures dans la
section 4.3. Les sections 4.4,4.5 et 4.6 parleront des accélérateurs graphiques, et des
techniques de programmation sur ceux-ci tandis que la section 4.7 définira CUDA
et donnera des exemples d’utilisation.

4.2 Les architectures parallèles et multi-cœurs

Les ordinateurs parallèles sont des machines qui comportent une architecture
parallèle, constituée de plusieurs processeurs identiques, ou non, qui concourent au
traitement d’une application. La performance d’une architecture parallèle est la
combinaison des performances de ses ressources et de leur agencement. (latence,
débit).

Quelques caractéristiques des architectures parallèles :


– pas de limite de mémoire ;
– pas de limite de processeurs ;
– accélération des calculs complexes ou coûteux en temps d’occupation CPU
(e.g. calcul matriciel, simulation numérique, transformée de Fourrier . . .) ;
– calcul répétitif sur un large ensemble de données structuré ;
– traitement indépendant ;

4.2.1 Classification des architectures

La classification la plus connue est celle de [9] flynn 1966, qui classifie les
architectures selon les flux de données (Single ou Multiple) et de contrôle (Single ou
Multiple).
• Machines SISD :
Une machine SISD (Single Instruction /Single Data) correspond concrètement à

30
4.2 Les architectures parallèles et multi-cœurs 31

Single Data (SD) Multiple Data (MD)


Single Instruction (SI) SISD SIMD
Multiple Instruction (MD) MISD MIMD

Table 4.1 – Taxonomie de Flynn 1966.

la machine séquentielle de Von Neumann. Une seule instruction est exécutée et


une seule donnée (simple, non-structurée) est traitée.

Figure 4.1 – Machine SISD.

• Machines SIMD :
Dans le modèle SIMD (Single Instruction stream, Multiple Data stream), on ré-
plique les unités de traitement pour effectuer exactement la même opération au
même moment sur des données différentes. Toutes les opérations parallèles d’une

SIMD Data Pool

PU
Instruction Pool

PU

PU

PU

Figure 4.2 – Machine SIMD

instruction sont exécutées de manière parfaitement synchrone sous le contrôle


d’une unité de contrôle centrale qui possède l’horloge de la machine SIMD. Pour
le programmeur, tout se passe comme si son programme s’exécutait sur une ma-
chine séquentielle classique, à la seule différence que les instructions exécutent des
31
4.2 Les architectures parallèles et multi-cœurs 32

opérations multiples au lieu d’opérations simples. Du point de vue de la program-


mation, les machines SIMD sont des machines séquentielles.
• Machines MISD :
N processeurs disposant chacun de sa propre unité de contrôle se partagent une
mémoire commune. Une machine MISD (Multiple Instruction/Single Data) de
calcul parallèle peut exécuter plusieurs instructions en même temps par plusieurs
unités fonctionnelles sur la même donnée.
Cela peut paraître paradoxal surtout que la réalité est que la donnée traitée durant
chaque étape du traitement dans le pipeline est différente de la précédente. Il
n’existe aucune machine construite sur ce modèle. C’est un modèle théorique sans
intérêt.

Figure 4.3 – Machine MISD.

• Machines MIMD :
Le cas des machines MIMD (Multiple Instruction /Multiple Data) est la classe la
plus générale et la plus intuitive. Il y a N processeurs, N flots d’instructions et
N flots de données. Ces machines ont un nombre de processeurs qui fonctionnent
de manière asynchrone et indépendante. A tout moment, différents processeurs
peuvent exécuter différentes instructions sur différentes données. Les machines
MIMD ainsi que SIMD sont appropriées aux techniques de parallélisme de don-
nées. Elles sont utilisées dans différents domaines d’applications tels que la concep-
tion assistée, la simulation et la modélisation.

32
4.3 Modèle de programmation parallèle 33

Figure 4.4 – Machines MIMD

4.2.2 Architectures multi-cœurs

Un microprocesseur multi-cœur (multi-core en anglais) est un processeur pos-


sédant plusieurs cœurs physiques. Depuis l’arrivée des premiers microprocesseurs
double cœurs en 2005, le nombre de cœurs ne cesse d’augmenter dans l’objectif
d’améliorer toujours plus la puissance des ordinateurs. On observe cependant un
certain nombre de problèmes dues à l’augmentation du nombre de cœurs, et cette
simple amélioration technique ne suffit parfois pas à améliorer de façon significative
les performances d’un ordinateur. Ces défis technologiques ne suffisent pourtant pas
à augmenter de façon significative les performances. Pour pouvoir en tirer profit, ce
matériel doit pouvoir être pris en compte par les différents systèmes d’exploitation.
Or, à l’heure actuelle, ces derniers n’arrivent pas à exploiter 100 % des possibilités.

4.3 Modèle de programmation parallèle

Il existe plusieurs modèles de programmation parallèle. En pratique un modèle


de programmation dépend principalement de plusieurs critères :
1. type de parallélisme à exploiter ;
2. modèle mémoire de la machine parallèle ;
3. facilité de programmation.
Il existe principalement deux modèles de programmation parallèle : modèle à
mémoire partagée et les modèles à mémoire répartie (passage des messages).

33
4.3 Modèle de programmation parallèle 34

Figure 4.5 – Architecture multicoeurs

La communication dans le modèle à mémoire partagée est implicite. L’informa-


tion est transmise lors de l’écriture dans une zone de la mémoire partagée, et récupé-
rée quand un autre processus vient lire cette zone. Par contre la synchronisation doit
être explicite. Le programmeur n’a pas besoin de spécifier la distribution des don-
nées sur les processeurs. Par contre, c’est à lui de spécifier le parallélisme et de gérer
la synchronisation des processeurs. Le programmeur doit utiliser des constructeurs
spécifiques ou des directives de programmation pour indiquer les régions parallèles
de son programme.

Dans le second modèle, chaque processus Pi possède sa propre mémoire privée


Mi . Il est le seul à y avoir accès. Les processus doivent alors communiquer pour
transférer de l’information à travers un média assurant ces transferts. La difficulté
de ce modèle provient de l’implantation du média. L’absence de mémoire partagée
engendre deux conséquences importantes pour le programmeur. Il doit lui-même gé-
rer la répartition des données sur les différents processeurs. L’échange d’informations
entre les processeurs nécessite la communication d’un message entre un émetteur et
un récepteur. C’est aux programmeurs d’identifier les émetteurs et les récepteurs et
de placer des fonctions de communication dans leurs programmes.

4.3.1 OpenMP

OpenMP (Open Multi-Processing) est une interface de programmation (API)


pour le calcul parallèle sur architecture à mémoire partagée [5]. Cette API est sup-
portée sur de nombreuses plateformes, (Unix et Windows, pour les langages de

34
4.3 Modèle de programmation parallèle 35

programmation C/C++ et Fortran). Il se présente sous la forme d’un ensemble de


directives, d’une bibliothèque logicielle et de variables d’environnement. OpenMP est
portable. Il permet de développer rapidement des applications parallèles en restant
proche du code séquentiel.

OpenMP est basé sur l’existence de plusieurs threads dans le paradigme de pro-
grammation à mémoire partagée. Un processus de mémoire partagée se compose de
plusieurs threads, c’est un modèle de programmation explicite (non automatique),
offrant au programmeur un contrôle total sur la parallélisation [28].

Tous les programmes OpenMP commencent comme un processus unique : le


thread maître. Le thread maître s’exécute séquentiellement jusqu’à ce qu’une région
parallèle soit rencontrée. Au début de cette région, le thread maître crée alors une
équipe de threads (fils d’exécution) parallèles. Lorsque les threads de l’équipe com-
plètent leur travail, ils se synchronisent et ne laissent que le thread maître continuer
l’exécution . Le listing 4.1 illustre une portion de code typique de la programmation
OpenMP.
#i n c l u d e <omp . h>
main ( ) {
i n t var1 , var2 , var3 ;
Code s é q u e n t i e l
/∗ Début de l a s e c t i o n p a r a l l è l e . C r é a t i o n d ’ une é q u i p e de
t h r e a d s . S p é c i f i c a t i o n de l a p o r t é e des v a r i a b l e s ∗/
#pragma omp p a r a l l e l p r i v a t e ( var1 , var2 ) s h a r e d ( var3 )
{ /∗ début de l a s e c t i o n p a r a l l è l e e x é c u t é e par t o u s l e s
t h r e a d s de l ’ é q u i p e ∗/
..
.
} /∗ Tous l e s t h r e a d s s e r e j o i g n e n t e t d i s p a r a i s s e n t .
S e u l l e t h r e a d master c o n t i n u e l ’ e x é c u t i o n ∗/
Code s é q u e n t i e l
}

Listing 4.1 – Structure générale d’un code openMP C/C++

Les directives OpenMP sont sensibles à la casse. Ils respectent les formats suivant
4.2.

35
4.3 Modèle de programmation parallèle 36

Table 4.2 – Formats des directives OpenMP.


# Pragma omp Nom-directive [ Clause ]
Obligatoire pour toutes Une directive valide Optionnelle. Les articles
les directives OpenMP OpenMP doit apparaître peuvent être dans n’im-
C/C++. après le pragma et avant porte quel ordre, et répéter
toute clause. au besoin.

4.3.2 Message Passing Interface (MPI )

MPI pour Message Passing Interface [30], conçue en 1993-94, est une norme
définissant une bibliothèque de fonctions, utilisable avec les langages C et Fortran.
Elle permet d’exploiter des ordinateurs distants ou multiprocesseurs par passage
de messages. MPI a été écrite pour obtenir de bonnes performances aussi bien
sur des machines massivement parallèles à mémoire partagée que sur des clusters
d’ordinateurs hétérogènes à mémoire distribuée [32]. Elle est disponible sur de très
nombreux matériels et systèmes d’exploitation. Ainsi, MPI possède l’avantage par
rapport aux plus vieilles bibliothèques de passage de messages d’être grandement
portable (car MPI a été implantée sur presque toutes les architectures de mémoires)
et rapide (car chaque implantation a été optimisée pour le matériel sur lequel il
s’exécute).
Son but est de spécifier une bibliothèque de fonctions de passage de messages
portable. Il se base sur un environnement d’exécution qui lance les processus et les
connecte entre eux. MPI supporte des modes de communication synchrones et asyn-
chrones et des communications collectives et offre des domaines de communication
séparés.
MPI est une librairie, pas un langage, c’est une spécification de librairie proposée
comme une norme pour une large population de vendeurs, développeurs et utilisa-
teurs. Il spécifie les noms, séquencement d’un appel et les résultats des fonctions
sous C, C++ et Fortran, et décrit ce qu’il faut faire mais pas comment faire.
Ce mode de programmation est connu sous le nom de : SPMD i.e. « Single Pro-
gramm Multiple Data » il consiste à lancer plusieurs processus sur une ou plusieurs
machines où chaque processus à ses propres variables locales. le partage de données
se fait par passage de message qui peut être :
i- Point à point (un à un) ;
ii- Collection de données (plusieurs à un) ;
iii- Broadcast (un à plusieurs).
36
4.3 Modèle de programmation parallèle 37

Ci-contre un exemple d’utilisation typique de MPI .

37
4.4 Les accélérateurs Graphiques GPU s 38

#i n c l u d e <s t d i o . h>
#i n c l u d e <mpi . h>

i n t main ( i n t argc , char ∗∗ argv ) {


i n t myrank , n p r o c s ;
MPI_Init(& argc , &argv ) ; // i n i t i a l i s a t i o n MPI
MPI_Comm_size(MPI_COMM_WORLD, &n p r o c s ) ; // Obtention du
nombre de p r o c e s s u s
MPI_Comm_rank(MPI_COMM_WORLD, &myrank ) ; // Obtntion de l
’ i d du p r o c e s s u s
p r i n t f ( " H e l l o from p r o c e s s o r %d o f %d\n " , myrank , n p r o c s
) ; // Chaque Pi a f f i c h e un H e l l o e t son i d
MPI_Finalize ( ) ; // F i n a l i s a t i o n MPI .
return 0;
}

Listing 4.2 – Hello World avec MPI

Fonction MPI Description


MPI_Init Initialisation de MPI
MPI_Finalize Finalisation de MPI
MPI_Comm_size Obtenir le nombre de
processus
MPI_Comm_rank Obtenir identificateur du
processus courant
MPI_Send Envoyer un message
MPI_Recv Recevoir un message

Table 4.3 – Fonction de base MPI.

4.4 Les accélérateurs Graphiques GPU s

Le GPU, de l’acronyme anglo-saxon Graphical Processing Unit, désigne le proces-


seur dédié au traitement des données graphiques. Avec l’augmentation gigantesque
de leurs performances, les GPU peuvent maintenant effectuer pléthore de calculs
parallèles. D’abord utilisé à des fins de décryptage, l’ancien compagnon du CPU

38
4.4 Les accélérateurs Graphiques GPU s 39

entre en concurrence directe avec son comparse et s’immisce dans l’industrie des
super-ordinateurs.

Généralement, le GPU est embarqué sur une carte dite graphique, mais il se
trouve de plus en plus intégré au chipset des cartes mères.

4.4.1 Le GPGPU

Le GPGPU qui signifie General-purpose Processing on GPU (traitements gé-


nériques sur processeurs graphiques) consiste à utiliser les processeurs graphiques
comme unité de calcul dans des applications autres que l’affichage de la 3D.

Alors que les CPU s sont performants dans l’exécution de tâches diverses et
variées les unes à la suite des autres (faible parallélisme) les GPU sont quant à
eux performants dans l’exécution de nombreuses tâches simples simultanées (fort
parallélisme).

L’encodage vidéo, qui consiste à appliquer un traitement identique à chacune des


images d’une vidéo, est un exemple typique de tâche fortement parallélisable.

Là où un CPU traitera les images de la vidéo l’une après l’autre (monothread,


2 à la fois sur dual-core, 4 à la fois sur quad-core, etc.) un GPU est quant à lui
capables de les traiter beaucoup plus rapidement (multi-thread massif : par exemple
120 images simultanément). Ceci est valable pour tout traitement décomposable en
un grand nombre de petits traitements similaires (les principales différences étant
les données, les instructions changeant très peu).

Avec l’évolution de leurs processeurs graphiques Nvidia et ATi, principaux fabri-


cants de GPU, ont pris conscience de cette possibilité il y a quelques années. Mais
pour pouvoir être utilisable sur un GPU, une application doit être capable de lui
fournir un grand nombre de traitements à effectuer en même temps : on dit d’une
application capable de ce type de comportement qu’elle est fortement threadée ou
encore fortement parallélisée. Mais pour être parallélisée une application a besoin
d’être développée spécialement dans ce sens. NVidia a été le premier à annoncer
puis à sortir sa technologie GPGPU : CUDA. CUDA est une librairie en langage C
(librairie propriétaire NVidia) qui permet aux développeurs qui l’utilisent de créer
des applications fortement parallélisées et qui seront donc exécutées sur les GPU
NVidia avec une efficacité redoutable. Fort de cette avance, CUDA est déjà utilisé
dans plus d’une centaine d’applications, véritable vitrine pour le GPGPU made in
NVidia.

39
4.4 Les accélérateurs Graphiques GPU s 40

Pour comprendre les différences en termes de performance entre le GPU et le


CPU, il faut regarder plus en détail l’architecture de chacun d’entre eux. Le CPU
est un processeur conçu pour effectuer des opérations séquentielles et généralement
variées. Son architecture (qui contient, entre autres, une unité arithmétique et lo-
gique, un séquenceur, des registres, une unité d’entrée-sortie, une mémoire cache)
permet l’exécution de programmes divers, à caractère général. De l’autre côté, l’ar-
chitecture du GPU a été optimisée pour effectuer une tâche bien précise : exécuter
un très grand nombre d’opérations massivement utilisées dans le domaine de la
modélisation et de l’affichage des images 3D (multiplications matricielles, filtres de
convolution, transformation des couleurs, etc. [21]).
Afin de comparer la vitesse des GPU avec celle des CPU, on utilise souvent
une mesure appelée FLOPS (FLoating point Operations Per Second), qu’on peut
traduire en français par opérations à virgule flottante par seconde. Bien que cette
mesure ne soit pas suffisante pour comparer les deux processeurs, elle peut quand-
même donner une idée sur la puissance de calcul de chacun d’entre eux. Dans la figure
4.4.1 ci-dessous on peut voir l’évolution en termes de vitesse des GPU de NVIDIA
et des CPU de Intel au cours des dernières années : On observe que les performances

Figure 4.6 – Comparaison entre GPU et CPU.

graphiques des GPU augmentent plus rapidement que celles des CPU. Ceci n’est
bien évidemment pas dû à la capacité de semi-conducteurs, qui augmente au même
rythme pour les deux plates-formes (et qui est étroitement liée aux progrès de la
technologie de fabrication). La différence est par contre dûe aux architectures de
ces deux types de processeurs, qu’on a brièvement décrites plus haut. Pour être plus
précis en ce qui concerne les processeurs graphiques, les GPU modernes adoptent un
40
4.5 BrookGPU 41

schéma de traitement parallèle de type SIMD/MIMD. Par exemple, dans la logique


SIMD, le même jeu d’instructions peut être exécuté simultanément sur un ensemble
d’unités de traitement synchronisées, ce qui confère un haut niveau de parallélisme.
De ce fait, seuls les algorithmes qui peuvent être adaptés peuvent bénéficier pleine-
ment des avantages offerts par ce type d’architecture.

4.5 BrookGPU

La première solution entièrement dédiée à la programmation GPGPU est le


BrookGPU [19], développé par des chercheurs de la Stanford University. BrookGPU
fonctionne comme une extension du C++ standard. A partir de fichiers sources
d’extension «.br », BrookGPU agit comme un compilateur transformant le code
source Brook en code C++ brut lié à une API au choix (Direct3D ou OpenGL). Le
langage reste relativement proche du langage graphique, en utilisant les streams et
les kernels.

4.6 OpenCL

OpenCL [23] est une combinaison d’une API et d’une extension du langage C.
Initialement conçu par Apple, puis affiné en partenariat avec AMD, Intel, et NVIDIA
- tous membres du Khronos Group - , OpenCL se présente aussi comme un standard
ouvert. Il s’agit d’un outil relativement récent. Le principe diffère un peu de Brook.
Tout d’abord, on pourra remarquer une ressemblance frappante, dans l’architecture
du code, avec l’utilisation d’un driver en C (création du device, puis création d’un
contexte . . . ). Cela s’explique du fait qu’OpenCL n’est pas destiné uniquement à
l’utilisation de la carte graphique, mais plutôt comme un outil généraliste permet-
tant de choisir son unité de calcul (le GPU , mais aussi le CPU ). On pourra aussi
remarquer que c’est dans l’instance du programme que l’on transmet le code à exé-
cuter sur le matériel choisi, le GPU dans notre cas. Le code n’est pas directement
intégré dans le programme.

4.7 CUDA

CUDA [25] est une architecture de traitement parallèle développée par NVI-
DIA permettant de décupler les performances de calcul du système en exploitant la
41
4.7 CUDA 42

puissance des processeurs graphiques (GPU ).

Alors que plus de 128 millions de GPU compatibles avec CUDA ont déjà été
vendus, des milliers de développeurs de logiciels, de scientifiques et de chercheurs
utilisent CUDA dans une grande gamme de domaines [18], incluant notamment la
production vidéo, l’astrophysique, la chimie et la biologie par modélisation numé-
rique, la mécanique des fluides numérique, les interférences électromagnétiques, la
reconstruction tomodensitométrique, l’analyse sismique, le ray tracing et bien plus
encore.

4.7.1 Contexte

Le calcul informatique a évolué en passant du traitement central exclusif des


CPU vers les capacités de co-traitement offertes par l’association du CPU et du
GPU. Pour permettre ce nouveau paradigme informatique, NVIDIA a conçu l’archi-
tecture de traitement parallèle CUDA, aujourd’hui incluse dans les GPU GeForce,
ION, Quadro et Tesla en offrant une base matérielle significative aux développeurs
d’applications.

Du côté du grand public, la plupart des applications majeures de traitement


vidéo, incluant des produits d’Elemental Technologies 1 , MotionDSP 2 et LoiLo, Inc 3 ,
sont accélérées par CUDA.

Du côté de la recherche scientifique, CUDA a été reçu avec enthousiasme. CUDApermet


par exemple d’accélérer AMBER, un programme de simulation de dynamique mo-
léculaire utilisé par plus de 60000 chercheurs du public et du privé afin d’accélérer
la découverte de nouveaux médicaments pour l’industrie pharmaceutique.

En matière de finance, Numerix et CompatibL ont annoncé leur support de


CUDApour une nouvelle application de recherche de risques de contrepartie, ac-
célérant jusqu’à 18× les procédures de calcul existantes. La plateforme Numerix est
utilisée par plus de 400 institutions financières.

Le parc existant de GPU Tesla, offrant d’importantes capacités en matière de


calcul par le GPU , permet également de jauger le succès de CUDA. Plus de 700
clusters de GPU sont aujourd’hui actifs dans le monde entier. De nombreux groupes,
allant de Schlumberger et Chevron (secteur énergétique) à BNP Paribas (secteur
1. solutions pour le traitement vidéo.
2. Utilisation du logiciel vReval qui améliore le rendu des séquences vidéo.
3. logiciels d’édition de vidéo.

42
4.7 CUDA 43

banquier) et figurant dans la liste des « 500 entreprises les plus importantes au
monde » publiée par Fortune, ont adopté CUDA.

Avec l’arrivée imminente de Windows 7 de Microsoft et de Snow Leopard d’Apple,


le calcul par le GPU est également une réalité pour le grand public. Dans ces nou-
veaux systèmes d’exploitation, le GPU ne tiendra pas seulement lieu de processeur
graphique, il jouera également un rôle de processeur parallèle pour toutes les appli-
cations.

4.7.2 Modèle de programmation CUDA

Pour un programmeur CUDA, le système informatique se compose d’un hôte qui


est une traditionnelle Unité centrale de traitement (CPU), et un ou plusieurs dispo-
sitifs qui sont équipés de processeurs massivement parallèles avec un grand nombre
d’unités d’exécution arithmétiques. Dans les applications logicielles modernes, il
existe souvent des parties de programme qui présentent le montant riche de paral-
lélisme de donnée, une propriété où de nombreuses opérations arithmétiques peut
être effectuée sans risque sur les structures de données dans un programme de ma-
nière simultanée. Les dispositifs CUDA accélèrent l’exécution de ces applications par
l’exploitation d’une quantité importante de de parallélisme de données.

4.7.3 Parallélisme de donnée

Les applications actuelles qui doivent traiter de grandes quantités de données


prennent beaucoup de temps à l’exécution. Ce temps pourrait être réduit en pa-
rallélisant les opérations : des phénomènes physiques peuvent être calculés indé-
pendamment les uns des autres, des images à analyser peuvent être découpées en
portions et un flux vidéo peut être découpé image par image.

La parallélisation des données réfère à la propriété du programme de gérer pa-


rallèlement et indépendamment ces instructions arithmétiques.

Par exemple, pour des multiplications de matrices de taille 1000 × 1000, il s’agit
de 1000000 de multiplications, sans rapport les unes avec les autres, qui peuvent donc
être parallélisées sans problème. Un GPU peut fortement améliorer les performances
en exécutant toutes ces opérations simultanément.

43
4.7 CUDA 44

4.7.4 Structure d’un programme CUDA

Un programme CUDA consiste en un ou plusieurs phases exécutés soit par le


hôte (CPU) soit par le périphérique (GPU ). Les phases qui ne comprennent pas
de parallélisme de données sont implémentées dans le code de l’hôte. Et celles avec
un flux important de parallélisme de données sont implémentées dans le code du
périphérique.
Le programme comprend un seul code source incluant les deux codes du péri-
phérique et du hôte. Le compilateur C de NVIDIA "NVCC" sépare les deux codes.
Le code source hôte est du stricte ANSI C et est compilé par les compilateur C stan-
dard et s’exécute comme un processus ordinaire. Le code du périphérique est écrit
en ANSI C étendu avec des mots-clés décrivant les fonctions parallèles de données
appelées Kernels et les structures de données associées. Ce code est compilé par le
"NVCC" puis exécuté par un GPU .
Les kernels génèrent un grand nombre de thread pour exploiter le parallélisme
de données. Il est à noter que les threads CUDA sont plus légers que ceux du CPU
en ce qui concerne les cycles de génération et d’ordonnancement.
La figure 4.7.4 montre un schéma général d’exécution d’un programme CUDA
typique.

Figure 4.7 – Exécution d’un programme CUDA.

L’exécution commence par une exécution au niveau de l’hôte (CPU). Quand un


Kernel est appelé, l’exécution est orienté vers le périphérique (GPU ), où un grand
nombre de thread est généré. Tous les threads générés par un kernel sont appelés une
grille. Quand tous ces threads terminent leurs exécution, la grille correspondante se
termine et l’exécution continue sur le hôte jusqu’à ce qu’un autre kernel est appelé.
44
4.7 CUDA 45

4.7.5 Mémoires périphériques et transfert de données

Dans CUDA, l’hôte et les périphériques ont des espaces mémoires séparés. Cela
reflète le fait que les dispositifs sont généralement des cartes matérielles qui viennent
avec leur propre Mémoire à accès aléatoire dynamique (DRAM). Afin d’exécuter
un kernel sur un périphérique, le programmeur doit allouer de la mémoire sur le
périphérique et transférer les données pertinentes de la mémoire hôte vers celle du
périphérique. De même, après l’exécution au niveau du périphérique, le programmeur
doit transférer les données résultat à partir de celui-ci à l’hôte et de libérer la mémoire
du périphérique qui n’est plus nécessaire. Le CUDA runtime fournit des appels
de fonctions dites "Application Programming Interface" (API) pour effectuer ces
transferts. La figure 4.7.5 présente une vue d’ensemble du modèle de mémoire du

Figure 4.8 – Vue générale du modèle de mémoire CUDA.

périphérique CUDA qui donne aux programmeurs la possibilité de raisonner sur la


répartition, le mouvement, et l’utilisation des différents types de mémoire disponibles
sur un périphérique. Au bas de l’image, nous voyons la mémoire globale et la mémoire
constante. Ce sont les mémoires que le code hôte peut écrire (W) et lire (R) à partir.
le périphérique accède seulement en lecture seule à la mémoire constante.
Le concept de modèle de mémoire CUDA est pris en charge par les fonctions API
qui peuvent être appelées par des programmeurs CUDA. La function CudaMalloc()
peut être appelée à partir du code hôte pour allouer de la mémoire globale pour
un objet. Son premier paramètre est un pointeur ver l’espace mémoire alloué pour
l’objet, le second indique la taille de cet objet.
Une fois qu’un programme a alloué de la mémoire du périphérique, le transfert
de données peut être effectué à partir de l’hôte vers la mémoire du périphérique
45
4.7 CUDA 46

grâce aux fonctions API CUDA pour le transfert des données entre les mémoires. La
fonction cudaMemcpy() nécessite quatre paramètres. Le premier est un pointeur vers
l’objet source de données à copier. Le second pointe l’emplacement de destination
pour l’opération de copie. Le troisième indique le nombre d’octets à copier. Tandis
que le quatrième paramètre indique les types de mémoire impliqués dans la copie :
de la mémoire de hôte vers la mémoire hôte, de la mémoire hôte vers la mémoire
périphérique, de la mémoire périphérique vers la mémoire hôte, et finalement de la
mémoire périphérique vers la mémoire périphérique.
cudaMemcpy (Md, M, s i z e , cudaMemcpyHostToDevice ) ;

Listing 4.3 – La fonction de copie mémoire CUDA

4.7.6 Les kernels et les threads

Nous discutons maintenant les fonctions kernels CUDA et l’organisation de


threads générées par l’invocation des kernels. Dans CUDA, un kernel spécifie le
code à exécuter par l’ensemble de threads d’une phase parallèle. Comme tous les
threads d’une phase parallèle exécute le même code, la programmation CUDA est
une instance de la bien-connue Single Program Multiple Data (SPMD) le style de
programmation parallèle pour les systèmes informatiques massivement parallèles.

Quand un kernel est invoquée, ou lancé, il est exécuté en tant que grille de
threads parallèles. Dans la figure 4.7.6, le lancement du Kernel 1 crée la grille 1.
Chaque grille CUDA comprend typiquement des milliers ou des millions de légers
threads GPU par appel kernel. Créer suffisamment de threads pour utiliser pleine-
ment le matériel nécessite souvent une grande quantité de parallélisme de données,
par exemple chaque élément d’un tableau de grande taille peut être calculé dans un
thread séparé.

Les threads dans une grille sont organisés en une hiérarchie à deux niveaux,
comme illustré à la figure 4.7.6. En réalité, une grille contient de nombreux threads
. Au niveau haut, chaque grille est constitué d’un ou de plusieurs blocs de threads.
Tous les blocs dans une grille ont le même nombre de threads. Dans la figure 4.7.6,
la grille 1 se compose de 6 blocs de thread qui sont organisés dans un tableau 2 × 3 à
deux dimensions. Chaque bloc a un thread de deux coordonnées tridimensionnelles
donnés par le blockIdx.x et le blockIdx.y qui sont des mots-clés spécifiques CUDA.
Tous les blocs doivent posséder le même nombre de threads organisées de la même
manière. Chaque bloc est à son tour organisé comme un tableau en trois dimensions
46
4.7 CUDA 47

Figure 4.9 – Organisation d’une grille CUDA.

de threads avec une taille totale d’un maximum de 512 threads. Les coordonnées
de threads dans un bloc sont uniquement définie par : threadIdx.x, threadIdx.y, et
threadIdx.z.
Très simplement, un kernel est une fonction exécutée sur le GPU . Il en existe diffé-
rent types, qualifiés de :

1. __global __
2. __device__
3. __host__

Le premier correspond à un kernel exécuté sur le GPU mais appelé par le CPU ;
le deuxième, à un kernel exécuté et appelé par le GPU ; le troisième, à une fonction
exécutée et appelée par le CPU. Ce dernier n’est pas obligatoire :
c’est le mode de fonctionnement par défaut.
Un appel de kernel se fait en spécifiant 2 paramètres entre triples chevrons pré-
cédant les paramètres passés au kernel.
k e r n e l <<< nBlocs , t h r e a d s P a r B l o c >>> ( arguments ) ;

Listing 4.4 – Un appel de kernel

nBlocs est le nombre de subdivisions appliquées à la grille à calculer et est de type


dim3 (le cast à partir d’un entier N initialise le dim3 à {N, 0, 0}).
47
4.7 CUDA 48

threadsParBloc indique le nombre de threads à exécuter simultanément pour


chaque bloc. Ici encore, cette valeur est de type dim3.

Les valeurs à appliquer dépendent simultanément du problème à résoudre (choix


des dimensions des blocs) et du matériel utilisé (nombre de threads par bloc). Choisir
un nombre de threads supérieurs à la quantité nativement supportée entraînera une
perte de performances. Cette notation permet ainsi d’adapter dynamiquement le
programme aux matériels passés, présents et futurs.

Chaque kernel dispose de variables implicites en lecture seule (toutes de type


dim3).

1. blockIdx : index du bloc dans la grille,


2. threadIdx : index du thread dans le bloc,
3. blockDim : nombre de threads par bloc (valeur de threadsParBloc du para-
métrage du kernel).

4.7.7 Exemple d’un programme CUDA

Voici un code source d’un programme typique CUDA. Il s’agit d’une addition
de deux vecteurs A et B de taille n dans un troisième vecteur C résultats. La grille
est sous-divisée en N blocs (tous de 1 dimension), l’index pourrait être trouvé de la
manière suivante4.5.
/∗ ∗ A d d i t i o n des v e c t e u r s A+B = C CUDA ∗ ∗/
#i n c l u d e <cuda . h>
#i n c l u d e <s t d i o . h>
#i n c l u d e <cuda_runtime . h>
const int N = 16;
const int blocksize = 16;

// k e r n e l d ’ a d d i t i o n e x é c u t é e par l e " d e v i c e "


__global__ v o i d add_vec ( f l o a t ∗ a , f l o a t ∗b , f l o a t ∗c , i n t
N ) {
i n t i = b l o c k I d x . x ∗ blockDim . x + t h r e a d I d x . x ;
c [ i ] = a[ i ] + b[ i ];
}

i n t main ( ) {
48
4.7 CUDA 49

f l o a t ∗a = new f l o a t [N ] ;
f l o a t ∗b = new f l o a t [N ] ;
f l o a t ∗ c = new f l o a t [N ] ;

f o r ( i n t i = 0 ; i < N; ++i ) {
a [ i ] = 1.0 f ; b [ i ] = 3.5 f ;
}

f l o a t ∗ad , ∗bd , ∗ cd ;
c o n s t i n t s i z e = N∗ s i z e o f ( f l o a t ) ;
cudaMalloc ( ( v o i d ∗∗)&ad , s i z e ) ; // A l l o c a t i o n de l a mémoire
d e v i c e pour l e v e c t e u r A
cudaMalloc ( ( v o i d ∗∗)&bd , s i z e ) ; // A l l o c a t i o n de l a mémoire
d e v i c e pour l e v e c t e u r B
cudaMalloc ( ( v o i d ∗∗)&cd , s i z e ) ; // A l l o c a t i o n de l a mémoire
d e v i c e pour l e v e c t e u r C

cudaMemcpy ( ad , a , s i z e , cudaMemcpyHostToDevice ) ; // c o p i e
du v e c t e u r A v e r s l e d e v i c e
cudaMemcpy ( bd , b , s i z e , cudaMemcpyHostToDevice ) ; // c o p i e
du v e c t e u r B v e r s l e d e v i c e
dim3 dimBlock ( b l o c k s i z e , 1 ) ; // nbr de t h r e a d s par b l o c k
dim3 dimGrid ( N/ dimBlock . x , 1 ) ; // nbr de b l o c k

add_vec<<<dimGrid , dimBlock>>>( ad , bd , cd , N ) ; // a d d i t i o n
au n i v e a u d e v i c e
cudaMemcpy ( c , cd , s i z e , cudaMemcpyDeviceToHost ) ; // c o p i e
du r é s u l t a t ( v e c t e u r C) du d e v i c e v e r s l e h o s t

/∗ A f f i c h a g e ∗/
f o r ( i n t i =0; i <N; i ++)
{
p r i n t f ( "%f \n " , c [ i ] ) ;
}
/∗ L i b e r a t i o n de l ’ e s p a c e mémoire s u r l e h o s t e t l e d e v i c e
∗/

49
4.8 conclusion 50

cudaFree ( ad ) ;
cudaFree ( bd ) ;
cudaFree ( cd ) ;
delete [ ] a ;
delete [ ] b;
delete [ ] c ;
r e t u r n EXIT_SUCCESS ;
}

Listing 4.5 – Addition de deux vecteurs

4.8 conclusion

On a vu dans ce chapitre qu’il existe plusieurs modes de programmation corrés-


pondants à plusieurs types d’architectures.

L’API OpenMP convient très bien à la programmation des architectures à mé-


moire partagée. Elles est simple à utiliser et elles se base sur les directives.

Tandis que MPI est désigné pour les architectures distribuées. C’est une norme
qui fait coordonner des processeurs distants par le passage de messages.

Une nouvelle voie vient de se tracer dans le domaine du parallélisme par l’avè-
nement de la technologie Nvidia et son architecture de traitement CUDA.

CUDA est une architecture de traitement parallèle définie pour l’exploitation des
cartes graphiques Nvidia pour des calculs autre que le traitement d’images.

50
Chapitre 5

Conception de la solution du
problème Q3AP

Sommaire
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.2 Méthode de résolution du Q3AP adoptée . . . . . . . . . 52
5.3 Solution du Q3AP et fonction d’évaluations . . . . . . . . 53
5.3.1 Solution du Q3AP . . . . . . . . . . . . . . . . . . . . 53
5.3.2 Fonction d’évaluation du Q3AP . . . . . . . . . . . 54
5.3.3 Fonction de voisinage . . . . . . . . . . . . . . . . . . 55
5.4 La nécessité du parallélisme . . . . . . . . . . . . . . . . . . 57
5.4.1 ILS-TS OpenMP . . . . . . . . . . . . . . . . . . . . . 57
5.4.2 ILS-TS-CUDA . . . . . . . . . . . . . . . . . . . . . . 59
5.4.3 ILS-TS-Multikernels . . . . . . . . . . . . . . . . . . 63
5.4.4 ILS-TS-multi GPU . . . . . . . . . . . . . . . . . . . 64
5.4.5 ILS-TS-MPI-cuda . . . . . . . . . . . . . . . . . . . . 64
5.4.6 ILS-TS-MPI-OpenMP-cuda . . . . . . . . . . . . . . 66
5.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

51
5.1 Introduction 52

5.1 Introduction

Dans ce chapitre nous allons parler des éléments qui constituent la solution au
problèmes d’optimisation traité (Q3AP), et de toutes les implémentations faites.

La section 5.2 représente la méthode de résolution adoptée et la section 5.3 donne


le codage de la solution et la fonction d’évaluation du problème ainsi que quelques
détailles d’ordre techniques. Quant à la section 5.4 définit les différentes versions
d’implémentation.

5.2 Méthode de résolution du Q3AP adoptée

Nous avons choisi d’hybrider deux métaheuristiques de résolution des problèmes


d’optimisation combinatoire qui sont :
– Une recherche locale itérée ; (Voir § 3.4.3.5)
– Une recherche tabou ; (Voir § 3.4.3.4)
L’hybridation des deux méthodes a été faite comme suit 6 : L’algorithme ILS-TS

Algorithm 6 Algorithme d’hybridation ILS-TS


1: s = s0 ; // Création d’une solution aléatoire ;
2: Eval(s) ; Evaluer la solution ;
3: répéter
4: Initialiser liste tabou ;
5: répéter
6: Trouver le meilleur voisin s = s0 ;
7: Mettre à jour la liste tabou ;
8: jusqu’à nombre d’itération maximum pour TS.
9: s∗ = P erturber(s0 ) ;
10: Eval(s∗ ) ;
11: jusqu’à nombre d’itération maximum pour ILS.
Sortie : Une solution approchée.

consiste à créer une solution aléatoire puis l’évaluer. Il applique sur elle une recherche
tabou (trouver le meilleur voisin et marquer le mouvement comme tabou) et donc
trouve un optimum local. Il perturbe celui-ci et il l’évalue. Ce processus est répétée
jusqu’à épuisement du nombre d’itération fixé au départ.

52
5.3 Solution du Q3AP et fonction d’évaluations 53

5.3 Représentation des solutions du problème Q3AP


et fonction d’évaluation

5.3.1 Solution du Q3AP

Nous optons pour une représentation directe des solutions du problème Q3AP
c - à - d une double permutation d’entiers de 0 à n − 1, n étant la taille du problème
à résoudre. Ces doubles permutations sont représentées par deux vecteurs de taille
variable comme montré au listing 5.1 suivant.
typedef struct {
i n t ∗ permut1 ;
i n t ∗ permut2 ;
} Solution ;

Listing 5.1 – Structure d’une solution

6 7 0 3 5 2 4 1
0 4 1 6 2 7 5 3

Figure 5.1 – Solution du Q3AP de taille N = 8

La création d’une solution initiale est simple, elle consiste à créer une solution aléa-
toire.
void c r e a t e ( Solution s o l u t i o n ) {
f o r ( i n t i = 0 ; i < Nd ; i ++){
s o l u t i o n . permut1 [ i ]= i ;
s o l u t i o n . permut2 [ i ]= i ;
}
f o r ( i n t i = 0 ; i < Nd ; i ++){
random = rand ( ) %(Nd−i ) + i ;
temp = s o l u t i o n . permut1 [ i ] ;
s o l u t i o n . permut1 [ i ] = s o l u t i o n . permut1 [ random ] ;
s o l u t i o n . permut1 [ random ] = temp ;
}
..
.
}

Listing 5.2 – Création d’une solution

53
5.3 Solution du Q3AP et fonction d’évaluations 54

5.3.2 Fonction d’évaluation du Q3AP

La fonction d’évaluation des solutions est définie par 5.3 :

i n t f u l l E v a l ( c o n s t i n t ∗ P , c o n s t i n t ∗ W, c o n s t S o l u t i o n
solution ){
i n t j j , l l ,mm, nn ;
int fitnessValue = 0;
f o r ( i n t i i = 0 ; i i < Nd ; i i++ ) {
j j = s o l u t i o n . permut1 [ i i ] ;
l l = s o l u t i o n . permut2 [ i i ] ;
f i t n e s s V a l u e = f i t n e s s V a l u e + Cost (P , W , i i , jj , ll
, ii , jj , ll ) ;
f o r ( i n t kk = 0 ; kk < Nd ; kk++ ) {
i f ( kk != i i ) {
nn = s o l u t i o n . permut1 [ kk ] ;
mm = s o l u t i o n . permut2 [ kk ] ;
f i t n e s s V a l u e = f i t n e s s V a l u e + Cost (P , W , i i , jj , ll ,
kk , nn , mm) ;
}
}
}
return fitnessValue ;
}

Listing 5.3 – fonction d’évaluation

Cette fonction (5.3) appelle par la suite une autre fonction :

i n t Cost ( c o n s t i n t ∗ P , c o n s t i n t ∗ W , i n t i , i n t j ,
i n t l , i n t k , i n t n , i n t m) ;

Listing 5.4 – fonction Cost appelée par fulleval

P et W sont les matrices de coût et de distances respectivement. Ils sont lues à


partir d’un fichier texte qui va contenir la taille du problème à résoudre.

La fonction MoveIncrEval() génère le voisinage et l’évalue, elle est définie comme


suit :
54
5.3 Solution du Q3AP et fonction d’évaluations 55

i n t MoveIncrEval ( c o n s t i n t ∗ A, c o n s t i n t ∗ B, c o n s t i n t ∗ S ,
int i , int j , int n , const int f i t n e s s ) ;

Listing 5.5 – La fonction d’évaluation incrémentale

Cette fonction 5.5 consiste à donner la solution voisine et à calculer sa fitness


sans faire recours à fulleval par l’appel de la fonction compute_delta().

5.3.3 Fonction de voisinage

Les méthodes de recherches locales génèrent toujours un nombre de voisinage


et l’exploite dans la recherche de l’optimum local. Dans cette section nous allons
définir notre fonction de voisinage.

La fonction de voisinage de la recherche tabou est définie par MovIncrEval()


comme cité précédemment (5.5). Cette fonction consiste à permuter deux positions
dans les deux permutations et déduire par la suite la fitness de la solution voisine à
partir de la solution précédente. Chaque solution à exactement (N d × (N d − 1)/2)2
voisins.

La perturbation au niveau de la recherche itérée est une permutation de deux


positions aléatoires sur la première ou la deuxième permutation.

La liste tabou est définie par un vecteur :

// one dimensional array tabu list|


int* tabu_list = (int*)malloc(sizeof(int)*Nd*(Nd-1)*Nd*(Nd-1)/4);

Et sa taille est égale à N d × (N d − 1) × N d × (N d − 1)/4 où N d est la taille du


problème à résoudre.

Les conditions d’arrêts des deux métaheuristiques sont les nombres d’itération
ILSmaxIter fixé à 100 et TSMAXITERS fixé à 3000 respectivement pour la recherche
itérée ILS et la recherche tabou TS.

Voici un organigramme décrivant les étapes essentielles de la méthode :

55
5.3 Solution du Q3AP et fonction d’évaluations 56

Lire ins-
tance Q3AP

Générer
une solution
initiale S0

Evaluer f (s0 )

Bst sol
oui
Fin trouvée
/approché

Générer
oui et évaluer les
s0 ← si
(n×((n−1))/2)2
voisins de S0

Existe-t-il
Perturber
un voisin
l’optimum local
Si de S0

Figure 5.2 – Organigramme de la méthode hybridée.

56
5.4 La nécessité du parallélisme 57

5.4 La nécessité du parallélisme

D’après l’organigramme 5.3.3, générer et évaluer les (N d × (N d − 1)/2)2 voisins


consommera beaucoup de temps de calcul ce qui nous a poussé a faire cette tache en
parallèle et donc penser au GPU. Le principal problème auquel nous nous sommes
confrontés est celui du « Mapping » entre threads GPU et le voisin à évaluer i.e
comment gérer la grille GPU et définir le nombre de block et de thread.

Par la suite, et pour mieux interpréter les résultats expérimentaux, nous avons
implémenter plusieurs versions de l’algorithme qui se ressemble algorithmiquement
parlant, et qui diffèrent au niveau de l’implémentation.

Ces versions sont :


ILS-TS séquentiel Qui est la version initiale ;
ILS-TS OpenMP une version parallèle exploitant l’architecture multi-cœurs ;
ILS-TS-CUDA une version parallèle exploitant l’architecture GPU ;
ILS-TS-CUDA-OpenMP une version parallèle multi-cœurs et GPU ;
ILS-TS-multikernels une version parallèle utilisant plusieurs kernels simultané-
ment ;
ILS-TS-multi GPU une version parallèle qui s’exécute sur plusieurs GPU s ;
ILS-TS-CUDA-MPI une version parallèle multiprocesseurs (MPI) -GPU ;
ILS-TS-OpenMP-MPI -CUDA une version parallèle exploitant OpenMPMPI et
CUDA.
Nous allons décrire brièvement quelques détails sur chacune des implémentations
précédentes.

5.4.1 ILS-TS OpenMP

Dans cette version parallèle de l’algorithme, nous essayons de profiter de l’atout


qu’offre la l’API OpenMP(voir ¶4.3.1).

Pour ce faire on a pensé à lancer plusieurs recherches ILS-TS en même temps


c’est - à - dire des recherches parallèles qui ont chacune sa fitness et qui coopèrent
ensuite pour trouver la meilleure solution parmi les solutions obtenues. (5.4.1).
ce qui devient a faire un schéma d’une recherche multistart. Chaque processus
OpenMPdémarre avec une solution initiale puis recherche une solution approché
par une méthode tabou, il perturbe la solution trouvée et la réévalue, puis il vérifie

57
5.4 La nécessité du parallélisme 58

S0 S1 Sn
...

TS TS TS

...

Meilleure Solution

Perturbation

.
.
.

Figure 5.3 – Shéma d’ILS-TS-OpenMP.

58
5.4 La nécessité du parallélisme 59

les critères d’arrêt à la fin de chaque ILS-TS et on choisie la meilleure solution parmi
les solution des processus. l’algorithme 5.6.
{
#pragma omp p a r a l l e l s h a r e d ( f i t _ t h r e a d s , P ,W) p r i v a t e (
new_fitness , solution , best_solution , f i n a l _ s o l u t i o n )
num_threads ( n b t h r e a d s )
{

..
.
create ( solution ) ;
/∗ e v a l u a t e th e s o l u t i o n ∗/
i n t b e s t _ f i t n e s s = f u l l E v a l (P ,W, s o l u t i o n ) ;

..
.
/∗ ILS s i m p l e p e r t u r b a t i o n
randomly s h u f f l e a c e r t a i n number o f p o s i t i o n s
i n t h e f i r s t permutation o r th e second permutation
∗/

..
.
#pragma omp c r i t i c a l
{
i f ( omp_get_thread_num ( ) ==0)
b e s t _ f i t n e s s=Minimum( f i t _ t h r e a d s ) ;
}

..
.
} // end o f p a r a l l e l r e g i o n pragma

Listing 5.6 – Pricncipaux étapes ILS–TS–OpenMP

5.4.2 ILS-TS-CUDA

Contrairement à la version précédente, cette version vise à paralléliser la tache de


génération évaluation des solutions voisines. Cette tache n’a pas pu être parallélisé
59
5.4 La nécessité du parallélisme 60

par OpenMPcar le nombre de voisins à générer (5.3.3) est de (N × (N − 1)/2)2 ) ce


qui devient un nombre important de processus à générer pour OpenMP.
La difficulté de la parallélisation au niveau CUDAréside dans le fait de trouver
un mapping optimal et une bonne exploitation des threads CUDA. On a opté pour
le schéma 5.4.2 suivant :
Lire ins-
tance Q3AP

Générer
une solution
initiale S0

Evaluer f (s0 )

Bst sol
oui
Fin trouvée
/approché

Générer
oui et évaluer les
s0 ← si Partie exécutée par GPU
(n×((n−1))/2)2
voisins de S0

Existe-t-il
Perturber
un voisin
l’optimum local
Si de S0

Figure 5.4 – Organigramme ILS-TS sous CUDA.

60
5.4 La nécessité du parallélisme 61

La difficulté réside dans la distribution des taches entre les différents threads du
GPU ,
{
__global__ v o i d MoveIncrEval ( c o n s t i n t ∗ P , c o n s t i n t ∗ W,
const Solution solution , int ∗ new_fitness )
{
v o l a t i l e i n t i d = b l o c k I d x . x ∗ blockDim . x + t h r e a d I d x . x ;
i f ( i d < Nd∗(Nd−1)∗Nd∗(Nd−1) /4 ) {
i n t move_first ;
i n t move_second ;
i n t move_third ;
i n t move_fourth ;
i n t temp , temp2 ;
temp = i d / (Nd∗(Nd−1) /2 ) ;
temp2 = i d % (Nd∗(Nd−1) /2 ) ;
m o v e _ f i r s t = (Nd−1) − f l o o r f ( ( ( s q r t f ( 8 ∗ ( ( Nd∗(Nd−1)
/ 2) − temp − 1 ) + 1 +0.1 f ) ) −1 ) / 2 ) − 1;
move_second = temp − m o v e _ f i r s t ∗ (Nd−1)+ m o v e _ f i r s t ∗ (
m o v e _ f i r s t + 1 ) /2 + 1 ;
move_third = (Nd−1) − f l o o r f ( ( ( s q r t f ( 8 ∗ ( ( Nd∗(Nd−1)
/ 2) − temp2 − 1 ) + 1 +0.1 f ) ) −1 ) / 2 ) − 1;
move_fourth = temp2 − move_third ∗ (Nd−1)+ move_third ∗
( move_third + 1 ) /2 + 1 ;
n e w _ f i t n e s s [ i d ] = compute_delta (P ,W, s o l u t i o n , move_second
, move_first , move_fourth , move_third ) ;
}
}

Listing 5.7 – Fonction de génération évaluation de voisinage CUDA

61
5.4 La nécessité du parallélisme 62

Et l’appel de ce kernel 5.8 se fait par :


/∗ copy on GPU t he c a n d i d a t e s o l u t i o n ∗/
cudaMemcpy ( s o l u t i o n _ d . permut1 , s o l u t i o n . permut1 , Nd∗
sizeof ( int ) ,
cudaMemcpyHostToDevice ) ;
cudaMemcpy ( s o l u t i o n _ d . permut2 , s o l u t i o n . permut2 , Nd∗
sizeof ( int ) ,
cudaMemcpyHostToDevice ) ;
MoveIncrEval<<<c e i l ( ( ( Nd∗(Nd−1)∗Nd∗(Nd−1) /4 ) ∗ 1 . 0 ) /
BLOCK_SIZE) ,BLOCK_SIZE>>>(new_fitness_d ) ;

/∗ copy th e f i t n e s s e s s t r u c t u r e i . e . t he e v a l u a t e d
solutions
from GPU t o CPU ∗/
cudaMemcpy ( n e w _ f i t n e s s , new_fitness_d , s i z e o f ( i n t ) ∗Nd
∗(Nd−1)∗Nd∗(Nd−1) / 4 ,
cudaMemcpyDeviceToHost ) ;

Listing 5.8 – Appel du kernel

Le mapping qu’on a choisi est le suivant :

MoveIncrEval<<<ceil(((Nd*(Nd-1)*Nd*(Nd-1)/4)*1.0)
/BLOCK_SIZE),BLOCK_SIZE>>>(new_fitness_d);

Pour pousser le parallélisme à son maximum et donc exploiter bien le GPU , on


essaye de traiter chaque voisin par un thread et donc on génère autant de voisins
que de threads ((N d × (N d − 1)/2)2 )). On a fixé donc le nombre de block à (N d ∗
(N d − 1) ∗ N d ∗ (N d − 1)/4)/ BLOCK_SIZE de taille BLOCK_SIEZ =256 chaqun.
La combinaison de ces deux méthodes précédentes consiste à lancer plusieurs
ILS-TS et de faire générer et évaluer leurs voisins au niveau du GPU ce qui la rend
une tache critique quand on dispose d’un seul GPU d’ou l’utilisation de la clause
"crtitical" :

#pragma omp critical


{

}
62
5.4 La nécessité du parallélisme 63

Ce qui signifie donc que chaque processus atteignant cette section critique bloquera
les autres processus d’où l’utilisation des versions multi-kernels et multi-GPU s sui-
vantes.

5.4.3 ILS-TS-Multikernels

Dans cette version, nous visons à combiner CUDA et OpenMP pour en profiter de
leurs avantages sans se faire pénalisé par le temps d’attente engendré par l’utilisation
de la section critique.

La démarche consiste à faire travailler autant de processus OpenMP que de


kernels CUDA. Mais cela semble irréalisable qu’avec des cartes graphiques NVidia
ayant un Compute capability ≥ 2.0.

Pour ce faire, CUDA propose la notion de « stream » ou flux qui va contenir les
kernels à exécuter :
cudaStream_t ∗ stream ;
// a l l o c a t i o n c r e a t i o n de stream
stream = ( cudaStream_t ∗) m a l l o c ( n k e r n e l s ∗ s i z e o f (
cudaStream_t ) ) ;
f o r ( i n t i =0; i <n k e r n e l s ; i ++)
{
c u t i l S a f e C a l l ( cudaStreamCreate(& stream [ i ] ) ) ;
}

Listing 5.9 – Création et utilisation d’un stream CUDA

Puis l’appel de kernel pour l’exécution de la fonction de recherche évaluation de


voisinage se fait par :

MoveIncrEval<<<ceil(((Nd*(Nd-1)*Nd*(Nd-1)/4)*1.0)
/BLOCK_SIZE),BLOCK_SIZE,0,stream[omp_get_thread_num()]>>>
(new_fitness_d+cp*nkernels);

Donc on passe au GPU les streams a exécuter comme paramètre pendant l’appel
du kernel.

63
5.4 La nécessité du parallélisme 64

5.4.4 ILS-TS-multi GPU

Cette version vise à utiliser les différents GPU s disponibles présents dans une
machine en créant autant de processus OpenMP que de GPU . On commence par
trouver le nombre de GPU présent sur une machine et créer ensuite un nombre de
processus OpenMP correspondant :
i n t devID ; // i d d ’ un gpu
i n t deviceCount =0; // compteur de GPU;
cudaGetDeviceCount(& deviceCount ) ;
p r i n t f ( " l e nombre de GPUs e s t :%d\n " , deviceCount ) ;
cudaDeviceProp d e v i c e P r o p s ; // p r o p r i e t é d ’ un GPU

Listing 5.10 – Utilisation du Multi–GPU

Et ensuite on spécifie le devID dans l’appel du kernel ppour l’affecter au GPU correspondant :

MoveIncrEval<<<ceil(((Nd*(Nd-1)*Nd*(Nd-1)/4)*1.0)
/BLOCK_SIZE),BLOCK_SIZE,omp_get_thread_num()>>>(new_fitness_d);

5.4.5 ILS-TS-MPI-cuda

Dans cette implémentation, Nous exploitons la possibilité qu’offre MPI en terme


d’utilisation des architectures distribuées. Nous avons donc pensé a utiliser plusieurs
machines simultanément pour résoudre notre problème et que chacune de ces ma-
chine possède un GPU et donc nous évaluons les voisinages d’une solution par
celui-ci. C’est donc une version hybridant MPI et CUDA. Pour ce faire, nous avons
procédé comme suit :
– Un fichier QAP_kernel_mpi.cu contenant les définition des kernels GPU. es-
sentielement la fonction run_kernel() ;
– et un fichier mpi_cuda_q3ap.cpp contenant l’appel de la fonction et donc la
recherche du voisinage sur GPU.
Voici la définition de la fonction run_kernel() :
f l o a t run_kernel ( i n t ∗ P , i n t ∗ W, S o l u t i o n s o l u t i o n , i n t ∗
new_fitness )
{
u n s i g n e d i n t timerILSTabuSearchGPU ;
cudaThreadSynchronize ( ) ;
64
5.4 La nécessité du parallélisme 65

CUT_SAFE_CALL( cutCreateTimer (&timerILSTabuSearchGPU ) ) ;


CUT_SAFE_CALL( c u t S t a r t T i m e r ( timerILSTabuSearchGPU ) ) ;
i n t ∗ Pd ;
cudaMalloc ( ( v o i d ∗∗)&Pd , Nd∗Nd∗ s i z e o f ( i n t ) ) ;
i n t ∗ Wd;
cudaMalloc ( ( v o i d ∗∗)&Wd, Nd∗Nd∗ s i z e o f ( i n t ) ) ;
cudaMemcpy (Pd , P , Nd∗Nd∗ s i z e o f ( i n t ) ,
cudaMemcpyHostToDevice ) ;
cudaMemcpy (Wd, W, Nd∗Nd∗ s i z e o f ( i n t ) ,
cudaMemcpyHostToDevice ) ;
Solution solution_d ;
cudaMalloc ( ( v o i d ∗∗)&s o l u t i o n _ d . permut1 , Nd∗ s i z e o f ( i n t ) ) ;
cudaMalloc ( ( v o i d ∗∗)&s o l u t i o n _ d . permut2 , Nd∗ s i z e o f ( i n t ) ) ;

/∗ Bind t e x t u r e memory on g l o b a l memory ∗/


cudaBindTexture (NULL, texRefPd , Pd) ;
cudaBindTexture (NULL, texRefWd , Wd) ;
/∗ Bind t e x t u r e memory on g l o b a l memory ∗/
cudaBindTexture (NULL, t e x R e f S o l u t i o n d 1 , s o l u t i o n _ d . permut1
) ;
cudaBindTexture (NULL, t e x R e f S o l u t i o n d 2 , s o l u t i o n _ d . permut2
) ;
/∗ a l l o c a t i o n on GPU o f t h i s s t r u c t u r e ∗/
i n t ∗ new_fitness_d ;
cudaMalloc ( ( v o i d ∗∗)&new_fitness_d , s i z e o f ( i n t ) ∗Nd∗(Nd−1)∗
Nd∗(Nd−1) /4 ) ;
/∗ copy on GPU t he c a n d i d a t e s o l u t i o n ∗/
cudaMemcpy ( s o l u t i o n _ d . permut1 , s o l u t i o n . permut1 , Nd∗ s i z e o f (
int ) ,
cudaMemcpyHostToDevice ) ;
cudaMemcpy ( s o l u t i o n _ d . permut2 , s o l u t i o n . permut2 , Nd∗ s i z e o f
( int ) ,
cudaMemcpyHostToDevice ) ;
MoveIncrEvalTexture<<< c e i l ( ( ( Nd∗(Nd−1)∗Nd∗(Nd−1) /4 )
∗ 1 . 0 ) /BLOCK_SIZE) ,BLOCK_SIZE>>>(/∗Pd ,Wd, s o l u t i o n , ∗/
new_fitness_d ) ;

65
5.4 La nécessité du parallélisme 66

/∗ copy th e f i t n e s s e s s t r u c t u r e i . e . t he e v a l u a t e d
solutions
from GPU t o CPU ∗/
cudaMemcpy ( n e w _ f i t n e s s , new_fitness_d , s i z e o f ( i n t ) ∗Nd
∗(Nd−1)∗Nd∗(Nd−1) / 4 ,
cudaMemcpyDeviceToHost ) ;
cudaThreadSynchronize ( ) ;
cutStopTimer ( timerILSTabuSearchGPU ) ;
cudaUnbindTexture ( texRefPd ) ;
cudaUnbindTexture ( texRefWd ) ;
cudaUnbindTexture ( t e x R e f S o l u t i o n d 1 ) ;
cudaUnbindTexture ( t e x R e f S o l u t i o n d 2 ) ;
cudaFree (Pd) ;
cudaFree (Wd) ;
cudaFree ( s o l u t i o n _ d . permut1 ) ;
cudaFree ( s o l u t i o n _ d . permut2 ) ;
cudaFree ( new_fitness_d ) ;
r e t u r n cutGetTimerValue ( timerILSTabuSearchGPU ) / 1 0 0 0 ;
}

Listing 5.11 – fonction run kernel()

Puis l’appel se fait par :

gputime=run_kernel(P,W,solution,new_fitness);

gputime pour calculer le temps passé dans un GPU .

5.4.6 ILS-TS-MPI-OpenMP-cuda

Pour exploiter tous les types de parallèlisme possible, on a visé à concevoir une
méthode qui exploite le parallélisme à mémoire distribué (MPI ), le parallélisme à
mémoire partagée (OpenMP) et le parallélisme de donnée sur GPU .

Cette implémentation essaye de lancer plusieurs recherches simultanées sur plu-


sieurs processeurs distants et de lancer dans ces processeurs, des processus OpenMP
de rechecherche ILS-TS et exploiter le voisinage de chaque recherche en utilisant le
GPU présent.

66
5.5 Conclusion 67

5.5 Conclusion

Ce que nous avons vu dans ce chapitre étais une conception de la méthode de


résolution pour servir par la suite comme élément de comparaison entre les différentes
architectures et techniques de programmation.

Le chapitre suivant intitulé " Expérimentation et résultats " sera consacré à cette
comparaison.

67
Chapitre 6

Expérimentation

Sommaire
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.2 Environnement d’exécution . . . . . . . . . . . . . . . . . . 69
6.3 Résultats sur les différentes architectures . . . . . . . . . 70
6.3.1 Résultats d’exécution de la version MPI -OpenMP-
CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.4 Interprétation des résultats . . . . . . . . . . . . . . . . . . 71
6.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

68
6.1 Introduction 69

6.1 Introduction

Ce chapitre définira les différents environnement d’exécution (6.2) et retracera les


différents résultats d’implémentations des versions de l’algorithme ILS-TS sus-cité
sur les différentes architectures qu’on a pu y accéder (6.3).

6.2 Environnement d’exécution

Les expérimentations que nous avons menées par la suite avaient pour objectif
de prouver que les unités graphiques peuvent être un moyen efficace pour accélérer
la recherche itérative de solutions du Q3AP. La méthode de recherche locale implé-
mentée sur GPU , rappelons le, est une recherche tabu itérative. La recherche tabu
est réitérée plusieurs fois pour améliorer la qualité des optimas locaux. Le principe
de l’approche est de perturber l’optimum local trouvé par la recherche tabu et de
l’utiliser comme solution initiale pour la recherche tabu suivante. La perturbation
implémentée est une suite de µ, de permutations opérées dans la première ou dans
la seconde permutation, µ étant un nombre entier aléatoire dans l’intervalle [2, n]
(n est la taille de l’instance). La méthode a été déployée sur deux configurations
matérielles différentes. La prmière configuration est un ordinateur portable AMD
Turion 64 X2 Mobile TL-60 @ 2000MHz doté d’une carte graphique Nvidia GeForce
8400M GS et la seconde est un PC de bureau Intel Core 2 Duo E7200 @ 2.53GHZ
X2 doté d’une carte graphique Nvidia Quadro NVS 290. Le nombre d’itérations de

Config. 1 Config. 2
VGA Nvidia GeForce 8400M GS Nvidia Quadro NVS 290
GPU clock 0.80 Ghz 0.92 Ghz
Taille Mém. 256 MB 256 MB
Major/Minor 1.1 1.1
Cuda Cores 2 MP × 8 cuda cores/MP 2 MP × 8 cuda cores/MP

Table 6.1 – Caractéristiques des deux configurations.

la recherche locale et celui de la recherche tabou ont été fixés respectivement à 100
m
et 3000. La taille de la liste tabou a été fixé à 4
. La méthode a été testé sur les
benchmarks nug8, nug12, nug13, nug15, nug18 et nug22 de la librairie QAPLIB [3]
Chaque instance e été exécuté 10 fois, les tableaux suivants servent comme com-
paraison entre les différentes versions implémentées et leurs impact sur le temps
d’exécution.
69
6.3 Résultats sur les différentes architectures 70

6.3 Résultats sur les différentes architectures

ILS-TS-CUDA
instance nug8 nug12 nug13 nug15 nug18 nug22
Opt/meill. val. 134 580 1912 2230 17836 42476
val. moyenne 134 688.4 2048.3 2488.33 18074 42745
Val. max. 134 776 2112 2544 18218 44116
Temps GPU (s) 1.48 11.27 19.15 46.07 68.28 234.21

Table 6.2 – Résultat d’exécution avec cuda.

le tableau 6.3 montre la combinaison entre OpenMPet CUDA :

instance nug8 nug12 nug13 nug15 nug18 nug22


ILS-TS-OpenMP-CUDA : 2 Threads
val. moyenne 134 621.71 1963 7860 18197 43231
Val. max. 134 694 1990 8060 18268 43742
Temps GPU (s) 2.69 14.32 22.12 55.11 141.80 474.63
ILS-TS-OpenMP-CUDA : 4 Threads
val. moyenne 134 584 1963 2013 18243 49480
Val. max. 134 604 1990 2046 20145 52119
Temps GPU (s) 2.65 15.25 30.07 14.08 164.17 549.84
ILS-TS-OpenMP-CUDA : 6 Threads
val. moyenne 134 597.67 1993 7530 18417 45412
Val. max. 134 710.00 2052 9340 20177 45723
Temps GPU (s) 4.21 22.28 47.64 63.40 420.12 1225.17
ILS-TS-OpenMP-CUDA : 8 Threads
val. moyenne 134 621.71 2050.50 8230.14 18083 46124
Val. max. 134 694 2094 9336 18162 47178
Temps GPU (s) 2.69 14.32 29.44 142.19 550.41 1378.19

Table 6.3 – Résultat d’exécution avec OpenMP et cuda.

6.3.1 Résultats d’exécution de la version MPI -OpenMP-


CUDA

Le tableau 6.3.1 suivant montre les résultats sur la version MPI -CUDA. Et la
version MPI -OpenMP-CUDAavec un nombre de thread variable (6.3.1).
70
6.4 Interprétation des résultats 71

ILS-TS-MPI -CUDA
instance nug8 nug12 nug13 nug15 nug18 nug22
val. moyenne 134 692.6 2033 2590.3 18210 46858
Val. max. 134 782 2112 2756 19147 48426
Temps GPU (s) 2.39 9.58 12.93 26.28 74.01 252.27

Table 6.4 – Résultat d’exécution avec MPI-CUDA.

instance nug8 nug12 nug13 nug15


ILS-TS-MPI -OpenMP-CUDA : 2 Threads
val. moyenne 134 599.2 2028.4 2567.6
Val. max. 134 652 2074 2616
Temps GPU (s) 2.39 6.58 11.06 16.91
ILS-TS-MPI -OpenMP-CUDA : 4 Threads
val. moyenne 134 612 2013.2 2606
Val. max. 134 638 2046 2690
Temps GPU (s) 2.4 7.61 11.09 16.83
ILS-TS-MPI -OpenMP-CUDA : 6 Threads
val. moyenne 134 670 2007.2 2518.4
Val. max. 134 742 2080 2582
Temps GPU (s) 2.39 8.08 11.08 16.81
ILS-TS-MPI -OpenMP-CUDA : 8 Threads
val. moyenne 134 666 2028.8 2588.8
Val. max. 134 772 2072 2666
Temps GPU (s) 2.39 8.08 12.88 16.99

Table 6.5 – Résultat d’exécution avec MPI-OpenMP-cuda.

6.4 Interprétation des résultats

On remarque que les résultats des versions ou on a combiné l’utilisation de CUDA


avec OpenMP, ne sont pas trop satisfaisantes, cela revient au partage de différents
threads d’un même bus PCI qui a un débit de transfert lent.

Les résultats de différentes versions montrent une amélioration importante en


terme de temps d’exécution aussi bien qu’en terme de qualité de solution.

La Version MPI-OpenMP-CUDA est la meilleure, cela est due à l’indépendance


des recherches multistart sur les deux configurations et donc la minimisation de
transfert GP U → CP U .

71
6.4 Interprétation des résultats 72

Voici quelques graphes illustrant la performance des différentes versions pour les
instances testés. On remarque que l’augmentation du nombre de threads (multi-

Figure 6.1 – résultats sur l’instance nug8.

START avec OpenMP) produit une baisse de performance en terme de temps d’exé-
cution (due à la charge sur le bus PCI qui relie la vRam avec la RAM). Tandis que les
versions MPI et MPI-OpenMP sont plus meilleures. Pour l’instance nug8, la version
CUDA reste la plus performante. Mêmes remarques que le nug8 mais on note bien

Figure 6.2 – résultats sur l’instance nug12.

qu’à partir de cette instance (nug12), la faveur reviens aux versions MPI-CUDA et
MPI-OpenMP-CUDA, cela revient à la minimisation significatif des transferts, dans
ces versions par rapport aux autres. L’instance nug15 montre une légère amélio-
ration dans la version OpenMP-CUDA 4 threads par rapport aux autres. Tandis
que l’instance nug12 suit le même schéma précédant. Ces graphes (nug18, nug22)
montrent bien, le rôle très important, que joue le multi-START dans les instances
de grandes tailles, et aussi l’amélioration très importante du temps d’exécution sur
le GPU.

72
6.5 Conclusion 73

Figure 6.3 – résultats sur l’instance nug13.

Figure 6.4 – résultats sur l’instance nug15.

Figure 6.5 – résultats sur l’instance nug18.

6.5 Conclusion

Au cours de ce mémoire, nous avons pu avoir l’opportunité d’étudier et d’exploi-


ter l’extraordinaire capacité de performances des architectures GPU et les techno-
logies de GPGPU.

73
6.5 Conclusion 74

Figure 6.6 – résultats sur l’instance nug22.

L’utilisation du GPU rend les application onéreuse en temps de calcul réalisable


même sur des PC de marché destinés au public.

On a vu une nette amélioration des performances par la combinaison des modes


de programmation parallèle (OpenMP MPI ) avec la version CUDA.

74
Chapitre 7

Conclusion

Nous avons abordé dans cette thèse, la conception et l’implémentation de mé-


thodes de recheche locale pour la résolution du problème d’affectation quadratique
à 3 dimensions (Q3AP), sur architectures multi et many-cœurs. Le choix de ce pro-
blème s’est basé sur ses retombées pratiques en premier lieu et le peu de travaux
dans la littérature qui traite ce problème.

Ce problème combinatoire est de type NP-difficile, il n’existe donc pas un algo-


rithme qui puisse résoudre ce problème en un temps polynomiale.

Résoudre ce problème fait donc appel aux métaheuristiques. Nous avons penchés
vers les recherches locales et plus exactement à la recherche locale itérative.

L’étape suivante de ce mémoire consistait a implémenter cette recherche sur un


environnement parallèle, ce qui nous a poussé aux accélérateurs graphiques (GPU )
et aux architectures multi-cœurs.

D’après les résultats d’exécution que nous avons obtenus, nous avons constaté
que ces architectures sont un moyen de parallélisme fort en terme de parallélisme de
données.

Rien qu’avec une carte graphique Nvidia grand public, on a pu avoir de bon
résultats en terme de temps d’exécution par rapport aux exécution séquentielle.

La combinaison de ces accélérateurs (GPU ) avec les mécanismes de parallélisme


des architectures multi-cœurs (MPI et OpenMP) s’est avérée importante en terme
de rapidité et d’accélération des temps d’exécutions.

En fin, le GPGPU est une solution de faible cout, à exploiter dans différents
domaines, scientifiques, mathématiques, et même médicale (étude de possibilité
d ?apparition des maladies héréditaires).

75
CHAPITRE 7. CONCLUSION 76

Les perspectives de ce travail consistent a tirer profit des mécanismes nouveaux


tels que : GPU-direct et OpenACC pour l’implémentation de ces recherches locales
et la meilleur exploitation des ressources disponibles actuellement tel les Cloud com-
puting et les grille de calculs.

76
Bibliographie

[1] ARPA and NSF. Mpi : A message-passing interface standard. In Message


Passing Interface Forum, 13 November 2003.
[2] N. Ayar. Métaheuristiques parallèles hybrides pour l’optimisation combina-
toire : problème de règles de golomb. Master’s thesis, Université de Jendouba,
Tunisie, 2010.
[3] R. E. Burkard, S. E. Karisch, and F. Rendl. Qaplib - a quadratic assignment
problem library. European Journal of Operational Research, (55) :115–119, 1991.
[4] S. Cagnoni, A. Bacchini, and L. Mussi. Opencl implementation of particle
swarm optimization : A comparison between multi-core cpu and gpu perfor-
mances. In EvoApplications, pages 406–415, 2012.
[5] R. Chandra, L. Dagum, D. K. D. Maydan, J. McDonald, and R. Menon. Parallel
Programming in OpenMP. MORGAN KAUFMANN PUBLISHERS, 2001.
[6] A. Colorni, M. Dorigo, and V. Maniezzo. Distributed optimization by ant
colonies. European Conference on Artificial Life ECAL91, pages 134–142, 1991.
[7] P. D’Alberto. A heterogeneous accelerated matrix multiplication : Opencl +
apu + gpu+ fast matrix multiply. 2012.
[8] M. Dorigo, V. Maniezzo, and A. Colorni. Positive feedback as a search strategy.
1991.
[9] M. J. Flynn. Computer Architecture : Pipelined and Parallel Processor Design.
Jones & Bartlett Publishers, Inc., 1995.
[10] D. B. Fogel. Evolutionary programming : an introduction and some current
directions. Statistics and Computing, June 1994.
[11] L. Fogel, A. Owens, and M. Wales. Artificial intelligence through simulated
evolution. John Wiley and Sons, October 1966.
[12] F. Glover. Tabu search : Part 1. ORSA Journal on Computing, 1(3) :190–206,
1989.

77
BIBLIOGRAPHIE 78

[13] D. E. Goldberg. Genetic algorithms in search, optimization and machine lear-


ning. 1989.
[14] P. Hahn, K. Bum-Jin, T. Stutzle, S. Kanthak, W. L. Hightower, H. Samra,
Z. Ding, and M. Guignard. The quadratic three-dimensional assignment pro-
blem : Exact and approximate solution methods. European Journal of Opera-
tional Research, 2008.
[15] P. M. Hahn, B.-J. Kim, T. Stützle, S. Kanthak, W. L. Hightower, Z. D. H.
Samra, and M. Guignard. The quadratic three-dimensional assignment pro-
blem : Exact and approximate solution methods. European Journal of Opera-
tional Research, (184) :416–428, 2008.
[16] K. Hawick and D. Playne. Hard-sphere collision simulations with multiple gpus,
pcie extension buses and gpu-gpu communications. Technical report, Institute
of Information and Mathematical Sciences Massey University Albany, 2011.
[17] J. H. Holland. Adaptation in natural and artificial systems. 1975.
[18] K. Iwai, N. Nishikawa, and T. Kurokawa. Acceleration of aes encryption on
cuda gpu. pages 131–145, 2012.
[19] C. Jiang and M. Snir. Automatic tuning matrix multiplication performance on
graphics hardware. 2005.
[20] r. T. P. S. Johann Dréo, Alain Petrowski. Métaheuristiques pour l’optimisation
difficile. Eyrolles, 2003.
[21] O. Julius, M. Frederic, and B. Ross. Implementation of kernel methods on the
gpu. In Proceedings of the Digital Image Computing on Techniques and Appli-
cations, DICTA ’05, pages 78–, Washington, DC, USA, 2005. IEEE Computer
Society.
[22] R. KAMMARTI. Approches Evolutionnistes Pour La Résolution Du 1-Pdptw
Statique Et Dynamique. PhD thesis, Ecole centrale de LILLE Université des
sciences et technologies de LILLE, 2006.
[23] J. Kim, S. Seo, J. Lee, J. Nah, G. Jo, and J. Lee. Opencl as a unified program-
ming model for heterogeneous cpu/gpu clusters. In PPOPP, pages 299–300,
2012.
[24] S. Kirkpatrick, C. D. Gelatt, and M. P. Vecchi. Optimization by simulated
annealing. Science, New Series, 220(4598) :671–680, 1983.
[25] A. Klöckner, N. Pinto, Y. Lee, B. C. Catanzaro, P. Ivanov, and A. Fasih. Pycuda
and pyopencl : A scripting-based approach to gpu run-time code generation.
pages 157–174, 2012.
78
BIBLIOGRAPHIE 79

[26] T. C. Koopmans and M. J. Beckmann. Assignment problems and the location


of economic activities. Econometrica, 25 :53–76, 1957.
[27] J. R. Koza and M. A. Keane. Genetic breeding of non-linear optimal control
strategies for broom balancing. In Proceedings of the Ninth International Confe-
rence on Analysis and Optimization of Systems. 1990, pages 47–56, 1990.
[28] O. Kwon, F. Jubair, R. Eigenmann, and S. P. Midkiff. A hybrid approach of
openmp for clusters. In PPOPP, pages 75–84, 2012.
[29] L. Loukil. Métaheuristiques hybrides parallèles pour le Q3AP sur environne-
ments à grande échelle. PhD thesis, Université d’Oran, Département d’Infor-
matique, 2010.
[30] Message Passing Interface Forum. MPI : A Message-Passing Interface Stan-
dard, Version 2.2. High Performance Computing Center Stuttgart (HLRS),
September 2009.
[31] X. Meyer. Etude et implémentation de l’algorithme du simplexe standard sur
gpus. Master’s thesis, Université de Genève, Février 2011.
[32] K. Nakajima. New strategy for coarse grid solvers in parallel multigrid methods
using openmp/mpi hybrid programming models. In PMAM, pages 93–102,
2012.
[33] W. P. Pierskalla. The multi-dimensional assignment problem. Technical Re-
port 93, Operations Research Department, CASE Institute of Technology, Sep-
tember 1967.
[34] I. Rechenberg. Cybernetic solution path of an experimental problem. Technical
report, Royal Air Force Establishment, 1965.
[35] H. Samra, Z. Ding, and P. Hahn. Symbol mapping diversity design for multiple
packet transmissions. IEEE Transactions on Communications, 53(5) :810–817,
2005.
[36] K. Sopyla, P. Drozda, and P. Górecki. Svm with cuda accelerated kernels for
big sparse problems. In ICAISC (1), pages 439–447, 2012.
[37] E. D. Taillard. Programmation à mémoire adaptative et algorithmes pseudo-
gloutons : nouvelles perspectives pour les méta-heuristiques. PhD thesis, Uni-
versité de Versailles Saint-Quentin-en-Yvelines, 1998.
[38] E. G. Talbi. Metaheuristic from design to implementation. John Wiley and
Sons, 2009.
[39] S. Verel. Métaheuristique pour l’optimisation difficile. cours, 2008.

79
Résumé

Le Problème d'affectation quadratique à 3 dimensions (Q3AP) est un problème d'optimisation


combinatoire (POC) difficile récurrent dans plusieurs domaines de la vie courante tel que : Le
routage dans les réseaux de télécommunication, affectation de ressources dans un processus
industriel la bio-informatique ou encore l'extraction de connaissances. Pour résoudre ce type de
problème, on fait recours aux méthodes de résolution des (POC) classifiées en deux grandes
classes : Méthodes exactes et Méthodes approchées. On s'intéresse dans nos travaux à la deuxième
classe et plus exactement aux métaheuristiques qui visent à obtenir des solutions de bonne qualité
en un temps raisonnable. On peut citer parmi ces méthodes, la recherche tabou, et les algorithmes
génétiques. En tenant compte des architectures existantes actuellement, on s'est penché vers les
accélérateurs graphiques (GPU) qui offrent désormais des atouts indéniables au sens de la
parallélisassions massive. Nous avons réalisé dans ce mémoire maintes versions d'implémentation
d'un algorithme de recherche tabou itérative en utilisant une panoplie d'architectures d'exécution
pour mettre en évidence l'avantage des GPU par rapport aux architectures classiques.

Mots clés :

Q3AP; QA; GPGPU; CUDA; GPU; Accélérateurs graphiques; Recherche tabou; Recherche
itérative; MPI; Open MP.

You might also like