• Aucun résultat trouvé

Modèle à mémoire partagée

Dans le document eCandy Evolutionary Camera Network Deploy (Page 51-55)

Algorithmes évolutionnaires

3.2 Algorithmes évolutionnaires distribués

3.2.1 Modèle à mémoire partagée

Pour ce modèle, les tâches partagent le même espace mémoire dans lequel elles lisent et écrivent de manière asynchrone. Chaque tâche est associée à un fil d'exécution, ou

"thread", et l'ensemble des tâches est toujours associé à un seul processus. Puisqu'ils partagent en partie les mêmes ressources, les communications entre fils d'exécution sont généralement plus efficaces que la communication entre processus. Le modèle permet d'exploiter facilement des ordinateurs à plusieurs cœurs en utilisant un fil d'exécution par cœur. En revanche, l'accès aux ressources partagées doit être synchronisé par des sémaphores ou des mutex. De plus, l'ordre d'accès à une ressource par plusieurs fils d'exécution est une fonction stochastique.

Design

Les opérateurs d'un algorithme évolutionnaire que l'on cherche à paralléliser ont sen- siblement tous la même forme. Cette forme présentée par l'algorithme 3.2 correspond aux opérateurs de croisement, de mutation, de sélection et d'évaluation. Il s'agit des quatre opérateurs auxquels on a appliqué le modèle à mémoire partagée. Un opérateur prend en arguments une population P et une probabilité prob. Ce dernier argument correspond à la probabilité que l'opération s'applique sur un individu. Seul l'opérateur d'évaluation n'est pas appliqué de manière stochastique. La fonction RANDOM corres-

O P E R A T O R ( P , prob)

1 for individual in P 2 do if RANDOM() < prob

3 then OPERATE(individual)

Algorithme 3.2: Patron d'opérateur.

OPERATOR (P, prob)

OPERATOR P 1 : N prob OPERATOR P .Y + l : N , prob

FIGURE 3.1 - Subdivision en deux tâches concourantes.

Le résultat d'une opération sur un individu n'influence pas les opérations sur les autres individus. La boucle d'application de l'opérateur peut donc être décomposée en un ensemble de tâches concourantes appliquant l'opération sur un sous-ensemble de la population. Pour deux tâches concourantes et une population P de N individus, on aurait donc l'exemple présenté à la figure 3.1.

L'ordre d'accès stochastique aux ressources partagées entraîne une complication au niveau de la parallélisation des opérateurs de croisement, mutation, et sélection puisqu'ils utilisent un générateur de nombres pseudo-aléatoires. Conceptuellement, un générateur de nombres pseudo-aléatoires dispose d'un état. Chaque fois qu'un nombre est généré, cet état est modifié. Ce changement d'état déterministe permet de garantir la reproductibilité des résultats à partir d'un état initial connu. Par contre, si plusieurs fils d'exécution se partagent l'accès à un générateur de nombres pseudo-aléatoires, le résultat obtenu pour un fil d'exécution est fonction de l'ordre d'accès au générateur. Puisque l'ordre d'accès à une ressource partagée est une fonction stochastique, le résultat obtenu d'un générateur de nombres pseudo-aléatoires par un fil d'exécution sera stochas- tique. Les résultats obtenus normalement par la parallélisation d'une tâche nécessitant un accès à un générateur des nombres pseudo-aléatoires sont donc non reproductibles. Afin de rendre reproductible le résultat de tâches concourantes utilisant des nombres pseudo-aléatoires, on applique le modèle de générateur de nombres pseudo-aléatoires distribué présenté par Foster [16]. Le modèle consiste à employer un générateur de nombres pseudo-aléatoires par fil d'exécution. Par contre, ce modèle à lui seul ne ga- rantit pas nécessairement la reproductibilité des résultats, il faut aussi que chaque fil

d'exécution soit associé à un sous-ensemble statique des données à traiter. Par exemple, pour un ensemble de six données sur lequel on applique une opération parallélisée en deux fils d'exécution ; pour que les résultats soient reproductibles, chaque fil d'exécution doit toujours traiter les mêmes données, i.e. : fil 1 : 1, 4, 5 ; fil 2 : 2, 3, 6.

Implementation

L'implémentation dans la librairie OpenBEAGLE d'opérateurs à traitement pa- rallèle a été réalisée à l'aide de la librairie OpenMP. OpenMP (Open Multi-Processing) est une interface de programmation permettant la programmation d'applications pa- rallèles dans les langages C/C++ et Fortran sur plusieurs systèmes d'exploitation. Le paradigme de programmation est basé sur l'utilisation de directives de compilation, de routines et de variables d'environnement.

La parallélisation d'une boucle semblable à celle présentée à la section précédente est très simple à l'aide d'OpenMP. On en présente ici un court exemple. La directive

"#pragma [...]" indique qu'on désire paralléliser la boucle qui suit la directive.

int main(int argc, char *argv[]) { const i n t N = 100000;

int a[N];

#pragma omp p a r a l l e l for for (int i = 0; i < N; ++i)

a [ i ] = 2 * i ; return 0;

}

Cette boucle sera parallélisée en un nombre arbitraire de fils d'exécution correspon- dant aux nombres de cœurs disponibles. La division de la tâche entre les fils d'exécution n'est cependant pas nécessairement uniforme, puisque les tâches sont distribuées en fonction du premier arrivé, premier servi. OpenMP permet de spécifier l'utilisation d'un algorithme d'ordonnancement statique des tâches et la taille des blocs de tâches assignés à chaque fil d'exécution. On suppose que les itérations pour une même tâche ont toutes en moyenne la même durée, on divise donc la tâche en n parties égales, où n correspond au nombre de fils d'exécution. En appliquant ces deux modifications à la boucle de base présentée plus haut, on obtient la boucle suivante :

FIGURE 3.2 - Utilisation du générateur de nombres pseudo-aléatoires distribué.

int chunk_size = max((int)(N / omp_get_max_threads()), 1); #pragma omp parallel for schedule(static, chunk_size)

for (int i = 0; i < N; ++i) a[i] = 2 * i;

En appliquant ces modifications, on garantit que chaque donnée sera toujours traitée par le même fil d'exécution, pour des nombres fixes de fils d'exécution et de données à traiter. Cette garantie était essentielle à l'implémentation du modèle de générateur de nombres pseudo-aléatoires distribué présentée à la section précédente.

L'accès au générateur de nombres aléatoires s'effectue par la classe System. Cette dernière regroupe un ensemble de composantes nécessaires au bon fonctionnement d'OpenBEAGLE. Puisque l'accès au générateur de nombres aléatoires est indirect, nous avons pu modifier OpenBEAGLE de manière à ce que l'utilisation du modèle partagé soit transparente pour l'usager. Nous avons implémenté un objet RandomizerMulti qui regroupe une liste de n générateurs de nombres pseudo-aléatoires, où n correspond au nombre de fils d'exécution. On présente à la figure 3.2 un exemple du fonctionnement de l'implémentation.

L'opérateur effectue une demande d'accès au générateur de nombres pseudo-aléa- toires à l'objet System. Ce dernier identifie ensuite quel est le numéro du fil d'exécution d'où provient la requête à l'aide des fonctionnalités d'OpenMP. System demande en- suite à l'objet RandomizerMulti de lui retourner le générateur de nombres pseudo- aléatoires associé au numéro du fil d'exécution. Finalement, le générateur est retourné à l'opérateur.

Dans le document eCandy Evolutionary Camera Network Deploy (Page 51-55)

Documents relatifs