<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
 "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
<!ENTITY howto        "http://www.traduc.org/docs/howto/lecture/">
<!ENTITY guide        "http://www.traduc.org/docs/guides/lecture/">
<!ENTITY addr-auteur  "hankd CHEZ engr POINT uky POINT edu">
<!ENTITY page-auteur  "http://dynamo.ecn.purdue.edu/~hankd/">
]>

<article lang="fr">

<articleinfo>
   <title>Le traitement en parallèle sous Linux</title>

   <subtitle>
      Version française du <foreignphrase>Parallel Processing HOWTO</foreignphrase>
   </subtitle>

   <author>
      <firstname>Hank</firstname>
      <surname>Dietz</surname>
      <email>&addr-auteur;</email>
   </author>

   <othercredit role="traduction" class="translator">
      <firstname>Dominique</firstname>
      <surname>van den Broeck</surname>
      <contrib>Adaptation française</contrib>
      <email>dvandenbroeck CHEZ free POINT fr</email>
   </othercredit>

   <othercredit role="relecture" class="translator">
      <firstname>Isabelle</firstname>
      <surname>Hurbain</surname>
      <contrib>Relecture de la version française</contrib>
      <email>isabelle POINT hurbain CHEZ pasithee POINT net</email>
   </othercredit>

   <othercredit role="publication" class="copyeditor">
      <firstname>Jean-Philippe</firstname>
      <surname>Guérard</surname>
      <contrib>Préparation de la publication de la v.f.</contrib>
      <email>fevrier CHEZ tigreraye POINT org</email>
   </othercredit>

   <releaseinfo>Version&nbsp;: 980105.fr.1.1</releaseinfo>
   <pubdate>4 septembre 2005</pubdate>

   <revhistory>
      <revision>
          <revnumber>980105.fr.1.1</revnumber>
          <date>2005-09-04</date>
          <authorinitials>DV</authorinitials>
          <revremark>Quelques améliorations mineures de la version 
          française</revremark>
      </revision>
      <revision>
          <revnumber>980105.fr.1.0</revnumber>
          <date>2004-09-13</date>
          <authorinitials>DV, IH, JPG</authorinitials>
          <revremark>Première traduction française</revremark>
      </revision>
      <revision>
          <revnumber>980105</revnumber>
          <date>1998-01-05</date>
          <authorinitials>HGD</authorinitials>
          <revremark>Version originale</revremark>
      </revision>
   </revhistory>

<abstract>

<para>

Le <emphasis>traitement en parallèle</emphasis> 
(<quote><foreignphrase>Parallel Processing</foreignphrase></quote>) se 
réfère à l'idée d'accélérer l'exécution d'un programme en divisant 
celui-ci en plusieurs fragments pouvant être exécutés simultanément, 
chacun sur son propre processeur, un programme exécuté sur 
<emphasis>N</emphasis> processeurs pouvant alors fonctionner 
<emphasis>N</emphasis> fois plus vite qu'il le ferait en utilisant un 
seul processeur. Ce document traite des quatre approches de base du 
traitement en parallèle accessibles aux utilisateurs de Linux&nbsp;: les 
systèmes Linux SMP, les grappes 
(<quote><foreignphrase>cluster</foreignphrase></quote>) de systèmes 
Linux mis en réseau, l'exécution en parallèle avec utilisation des 
instructions multimédia (ex&nbsp;: MMX), et l'utilisation des 
processeurs secondaires embarqués dans une machine fonctionnant sous 
Linux.

</para>

</abstract>

</articleinfo>

<sect1>
<title>Introduction</title>

<para>

L'<emphasis>exécution en parallèle</emphasis> 
(<quote><foreignphrase>Parallel Processing</foreignphrase></quote>) se 
rapporte à l'idée d'accélérer l'exécution d'un programme en le divisant 
en plusieurs fragments pouvant être exécutés simultanément, chacun sur 
son propre processeur. Un programme exécuté sur <emphasis>N</emphasis> 
processeurs pourrait alors fonctionner <emphasis>N</emphasis> fois plus 
vite qu'il ne le ferait en utilisant un seul processeur.

</para>

<para>

Traditionnellement, les processeurs multiples sont fournis avec un 
<quote>ordinateur en parallèle</quote> spécialement conçu. De ce fait, 
Linux gère désormais les systèmes <emphasis>SMP</emphasis> (souvent 
vendus en tant que <quote>serveurs</quote>) dans lesquels plusieurs 
processeurs partagent le même bus et la même mémoire au sein d'un même 
ordinateur. Il est également possible à un groupe d'ordinateurs (par 
exemple un groupe de PC fonctionnant chacun avec Linux) d'être 
interconnectés par un réseau pour former un ensemble de traitement en 
parallèle (<quote><foreignphrase>cluster</foreignphrase></quote>). La 
troisième alternative pour l'exécution parallèle sous Linux est 
l'utilisation du jeu d'instructions multimédias étendu (MultiMedia 
Extend&nbsp;: MMX) pour agir en parallèle sur des vecteurs de données 
entières. Il est enfin possible d'utiliser un système Linux comme 
<quote>hôte</quote> hébergeant un moteur de calcul en parallèle 
<emphasis>dédié</emphasis>. Toutes ces approches sont traitées en détails 
dans ce document.

</para>

<sect2>
<title>Le traitement en parallèle correspond-il à mes besoins&nbsp;?</title>

<para>
Bien que l'emploi de multiples processeurs puisse accélérer nombre
d'opérations, la plupart des applications ne peuvent encore tirer
profit du traitement en parallèle. A la base, le traitement en
parallèle est approprié si&nbsp;:
</para>

<para>

<itemizedlist>
<listitem>

<para>
Votre application est suffisamment parallélisée pour faire bon
usage de multiples processeurs. C'est, en partie, une question
d'identification des différentes portions du programme pouvant
être exécutées indépendamment et simultanément sur des processeurs
séparés, mais vous découvrirez aussi que certaines choses qui
<emphasis>peuvent</emphasis> être exécutées en parallèle
ralentissent le traitement si elles sont exécutées en parallèle
sur un système particulier. Par exemple, un programme qui
s'exécute en quatre secondes sur une machine unique pourrait
être capable de n'occuper qu'une seconde du temps de chacune
de quatre machines, mais n'apportera pourtant aucun gain de
temps s'il faut trois secondes ou plus à ces machines pour
coordonner leurs actions.

</para></listitem>

<listitem><para>

Soit l'application qui vous intéresse en particulier a été
<emphasis>parallélisée</emphasis>, c'est-à-dire réécrite
pour tirer profit du traitement en parallèle, soit vous comptez
produire au moins un peu de code original qui le fasse.

</para></listitem>

<listitem><para>

Vous êtes intéressé par la recherche de nouvelles solutions impliquant 
un traitement en parallèle, ou au moins souhaitez vous familiariser 
avec. Le traitement en parallèle sous Linux n'est pas forcément 
difficile, mais ce n'est pas une notion familière à beaucoup 
d'utilisateurs, et il n'existe pas de livre intitulé <quote>Le 
<foreignphrase>Parallel Processing</foreignphrase> pour les 
nuls</quote>, en tout cas pas encore. Ce guide est un bon point de 
départ, mais il ne contient pas l'intégralité de ce que vous devez 
connaître.

</para></listitem>

</itemizedlist>

</para>

<para>

La bonne nouvelle, c'est que si tout ce qui vient d'être dit est vrai, 
vous découvrirez cependant que le traitement en parallèle sous Linux 
peut apporter les performances d'un supercalculateur à des programmes 
effectuant des opérations complexes ou travaillant sur de très grandes 
quantités de données. Mais en plus, cela peut être fait en utilisant du 
matériel peu onéreux et que vous possédez sûrement déjà. Avec çà, il 
reste aisé d'utiliser un système de traitement en parallèle sous Linux à 
d'autres choses lorsqu'il n'est pas en train d'accomplir un traitement 
en parallèle.

</para>

<para>

Si le traitement en parallèle ne correspond <emphasis>pas</emphasis> à 
vos besoins, mais que vous souhaitez tout de même améliorer sensiblement 
les performances de votre machine, il reste des choses que vous pouvez 
faire. Par exemple, vous pouvez améliorer les performances d'un 
programme séquentiel en remplaçant votre processeur par un plus rapide, 
en ajoutant de la mémoire, en remplaçant un disque IDE par un 
<quote><foreignphrase>fast wide SCSI</foreignphrase></quote>, et cætera. 
Si c'est ce qui vous intéresse, sautez directement à la section 6.2, 
sinon poursuivez votre lecture.

</para>

</sect2>

<sect2>
<title>Terminologie</title>

<para>

Bien que le traitement en parallèle ait été utilisé pendant de 
nombreuses années par de nombreux systèmes, il reste étranger à la 
plupart des utilisateurs. Aussi, avant de traiter des différentes 
alternatives, il est important de se familiariser avec une poignée de 
termes usuels&nbsp;:

</para>

<variablelist>

<varlistentry>
<term>SIMD&nbsp;:</term>
<listitem><para>

SIMD (<quote><foreignphrase>Single Instruction stream, Multiple Data stream</foreignphrase></quote>, ou
<quote>Un seul flot d'instruction, plusieurs flots de données</quote>),
fait référence à un modèle d'exécution en parallèle dans
lequel tous les processeurs traitent la même opération à
la fois, mais où chaque processeur est autorisé à agir sur
sa propre donnée. Ce modèle convient naturellement au concept
où l'on applique le même traitement sur chaque élément d'un
tableau, et est ainsi souvent associé à la manipulation de
vecteurs ou de tableaux. Toutes les opérations étant
implicitement synchronisées, les interactions entre processeurs
SIMD tendent à être facilement et efficacement mises en &oelig;uvre.

</para></listitem>
</varlistentry>

<varlistentry>
<term>MIMD&nbsp;:</term>
<listitem><para>

MIMD (<quote><foreignphrase>Multiple Instruction stream, Multiple Data stream</foreignphrase></quote>,
ou <quote>Plusieurs flots d'instructions, plusieurs flots de données</quote>),
se rapporte au modèle d'exécution en parallèle dans lequel chaque
processeur agit essentiellement seul. C'est le modèle qui convient
le mieux à la décomposition d'un programme pour une exécution en
parallèle sur une base fonctionnelle. Par exemple, une processeur
peut mettre à jour une base de données pendant qu'un autre produit
l'affichage graphique de la nouvelle entrée. C'est un modèle plus
flexible que l'exécution en SIMD, mais qui s'accomplit au risque
d'un cauchemar pour le débogueur, les <quote><foreignphrase>race conditions</foreignphrase></quote>
ou <quote>accès concurrents</quote>, dans lesquels un programme peut planter
de façon intermittente à cause des différences de minutage entre
les opérations des différents processeurs lorsque celles d'un de ces
processeurs sont réorganisées en fonction de celles d'un autre.

</para></listitem>
</varlistentry>

<varlistentry>
<term>SPMD&nbsp;:</term>
<listitem><para>

SPMD (<quote><foreignphrase>Single Program, Multiple Data</foreignphrase></quote>),
ou <quote>Un seul programme, plusieurs données</quote>, est une version
restreinte du MIMD dans laquelle tous les processeurs
exécutent le même programme. Contrairement au SIMD, chaque
processeur peut suivre un chemin différent dans le programme.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Bande passante&nbsp;:</term>
<listitem><para>

La bande passante 
(<quote><foreignphrase>bandwidth</foreignphrase></quote>) d'un système 
de communication correspond à la quantité maximum de données que l'on 
peut transmettre en une unité de temps&hellip; une fois que la 
transmission de données a commencé. La bande passante des connexions 
série est souvent mesurée en <emphasis>bauds</emphasis> ou en 
<emphasis>bits par seconde (b/s)</emphasis>, ce qui correspond en 
général à huit fois ou dix fois le nombre d'<emphasis>octets par 
secondes (O/s ou B/s</emphasis>, B = 
<quote><foreignphrase>Byte</foreignphrase></quote> = 
<quote>Octet</quote>). Par exemple, un modem à 1200 bauds (N.D.T.&nbsp;: 
comme celui du Minitel) peut transférer environ 120 octets à la seconde 
(B/s), tandis qu'une connexion réseau ATM à 155 Mb/s est environ 
130&nbsp;000 fois plus rapide, en transférant environ 17&nbsp;Mo/s. Une bande 
passante élevée permet un transfert efficace de larges blocs de données 
entre processeurs.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Latence&nbsp;:</term>

<listitem><para>

La latence d'un système de communication représente le temps minimum 
nécessaire pour la transmission d'un objet, en incluant toutes les 
données <quote>forfaitaires</quote> logicielles pour l'émission et la 
réception (<quote><foreignphrase>overhead</foreignphrase></quote>). Le 
temps de latence est très important dans les traitements en parallèle 
car il détermine la <emphasis>granularité</emphasis>, la 
durée minimum d'exécution d'un segment de code pour gagner en vitesse 
d'exécution grâce au traitement en parallèle. Concrètement, si un 
segment de code s'exécute en moins de temps qu'il n'en faut pour 
transmettre son résultat (ce délai-ci formant la latence), exécuter ce 
segment en série plutôt qu'en parallèle sera plus rapide puisqu'il n'y 
aura pas de délai de transmission.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Envoi de messages (<quote><foreignphrase>Message passing</foreignphrase></quote>)&nbsp;:</term>

<listitem><para>

Les envois de message sont un modèle d'interaction entre les différents 
processeurs d'un système parallèle. En général, un message est construit 
logiciellement sur un processeur et envoyé via une interconnexion réseau 
à un autre processeur. Bien que le surcoût en temps 
(<quote><foreignphrase>overhead</foreignphrase></quote>) engendré par la 
gestion de chaque message (ce délai formant la latence) soit élevé, 
typiquement, il n'y a que peu de restrictions quant à la quantité 
d'informations que ce message peut contenir. Ainsi, l'envoi de messages 
peut assurer une bande passante élevée en apportant une méthode efficace 
pour transmettre de grandes quantités d'informations d'un processeur à 
un autre. En revanche, afin de réduire les besoins en coûteuses 
opérations d'envoi de message, les structures de données à l'intérieur 
d'un programme doivent être réparties à travers tous les processeurs de 
façon à ce que la plupart des données référencées par chaque processeur 
se trouve dans sa mémoire locale&hellip; Cette tâche porte le nom de 
<quote>répartition des données</quote> (<quote><emphasis>data 
layout</emphasis></quote>).

</para></listitem>
</varlistentry>

<varlistentry>
<term>Mémoire partagée&nbsp;:</term>

<listitem><para>

La mémoire partagée est elle aussi un modèle d'interaction entre les 
processeurs d'un système parallèle. Les systèmes comme les machines 
biprocesseurs Pentium faisant fonctionner Linux partagent 
<emphasis>physiquement</emphasis> la même mémoire entre tous leurs 
processeurs, si bien qu'une valeur écrite par un processeur est 
directement accessible par un autre processeur. A l'inverse, la mémoire 
partagée <emphasis>logiquement</emphasis> peut être implémentée sur les 
systèmes où chaque processeur dispose d'une mémoire qui lui est propre, 
en convertissant chaque référence à une zone non locale de la mémoire en 
une communication inter-processeur appropriée. Cette implémentation de 
la mémoire partagée est généralement considérée comme étant plus facile 
à utiliser que les files de messages. La mémoire partagée physiquement 
peut présenter à la fois une bande passante élevée et des temps de 
latence très bas, mais seulement lorsque les différents processeurs 
n'essaient pas d'accéder au bus simultanément. Ainsi, le modèle de 
répartition des données peut avoir une sérieuse influence sur les 
performances, et les effets de cache et autres peuvent rendre très 
difficile la détermination du meilleur modèle.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Fonctions d'agrégation (<foreignphrase>Aggregate Functions</foreignphrase>)&nbsp;:</term>
<listitem><para>

Dans le modèle des files de messages comme dans celui de la mémoire 
partagée, une communication est initiée par un processeur seul. Par 
contraste, une fonction d'agrégation est un modèle implicitement 
parallèle dans lequel tous les processeurs d'un groupe agissent 
ensemble. Le cas le plus simple est celui des <emphasis>barrières de 
synchronisation</emphasis>, dans lequel chaque processeur se met en 
attente jusqu'à ce que le groupe entier ait atteint la barrière. Si 
chaque processeur émet une donnée en atteignant une barrière, il est 
possible de demander à l'électronique responsable des communications 
d'émettre en retour une valeur à chaque processeur, valeur qui pourrait 
être fonction des données collectées sur tous les processeurs. Par 
exemple, la valeur de retour pourrait être la réponse à la question 
<quote>Est-ce qu'un processeur a trouvé la réponse&nbsp;?</quote> ou 
pourrait être la somme d'une valeur propre à chaque processeur. Les 
temps de latence peuvent être bas, mais la bande passante a tendance à 
être basse elle aussi. Traditionnellement, ce modèle est surtout utilisé 
pour contrôler l'exécution en parallèle, plutôt que pour distribuer les 
données.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Communication collective&nbsp;:</term>
<listitem><para>

C'est un autre nom pour les fonctions d'agrégation, utilisé le plus 
souvent en référence à celles qui sont construites en utilisant de 
multiples opérations d'envoi de message.

</para></listitem>
</varlistentry>

<varlistentry>
<term>SMP&nbsp;:</term>
<listitem><para>

SMP (<quote><foreignphrase>Symmetric 
Multi-Processor</foreignphrase></quote>) se rapporte au concept d'un 
groupe de processeurs travaillant ensemble en tant qu'homologues, si 
bien que chaque partie d'un travail peut être effectuée de la même façon 
par n'importe quel processeur de ce groupe. Typiquement, le SMP implique 
la combinaison du MIMD et de la mémoire partagée. Dans l'univers IA32, 
SMP signifie souvent <quote>compatible MPS</quote> 
(<quote><foreignphrase>Intel MultiProcessor 
Specification</foreignphrase></quote>). À l'avenir, cela pourrait 
signifier 
<quote><foreignphrase>Slot&nbsp;2</foreignphrase></quote>&hellip;

</para></listitem>
</varlistentry>

<varlistentry>
<term>SWAR&nbsp;:</term>
<listitem><para>

SWAR (<quote><foreignphrase>SIMD Within A 
Register</foreignphrase></quote>, ou <quote>SIMD à l'intérieur d'un 
registre</quote>) est un terme générique qui désigne le concept 
consistant à partitionner un registre en plusieurs champs entiers et à 
effectuer des opérations sur toute la largeur du registre pour faire du 
calcul en parallèle SIMD sur tous les champs à la fois. En considérant 
une machine avec des registres longs de <emphasis>k</emphasis> bits (et 
donc autant pour les chemins de données, et les unités des fonctions), 
on sait depuis longtemps que les opérations sur les registres ordinaires 
peuvent fonctionner comme des opérations parallèle SIMD sur 
<emphasis>n</emphasis> champs de <emphasis>k/n</emphasis>&nbsp;bits. Bien que ce 
type de parallélisme puisse être mis en &oelig;uvre en utilisant les 
registres entiers et les instructions ordinaires, plusieurs modèles 
récents de microprocesseurs intègrent des instructions spécialisées pour 
améliorer les performances de cette technique pour des tâches orientées 
multimédia. En plus du <emphasis>MMX</emphasis> 
(<quote><foreignphrase>MultiMedia eXtension</foreignphrase></quote>) 
d'Intel/AMD/Cyrix, il existe&nbsp;: <emphasis>MAX</emphasis> 
(<quote><foreignphrase>MultimediA eXtension</foreignphrase></quote>) sur 
l'Alpha de Digital, <emphasis>MAX</emphasis> 
(<quote><foreignphrase>Multimedia Acceleration 
eXtension</foreignphrase></quote>) sur le PA-RISC de Hewlett-Packard, 
<emphasis>MDMX</emphasis> (<quote><foreignphrase>Digital Media 
eXtension</foreignphrase></quote>, prononcé <quote>Mad&nbsp;Max</quote>) 
sur MIPS, et <emphasis>VIS</emphasis> (<quote><foreignphrase>Visual 
Instruction Set</foreignphrase></quote>) sur le SPARC V9 de Sun. En 
dehors des trois constructeurs qui ont adopté le MMX, tous ces jeux 
d'instructions sont comparables, mais incompatibles entre eux.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Processeur auxiliaires, dédiés. (<quote><foreignphrase>Attached Processors</foreignphrase></quote>)&nbsp;:</term>
<listitem><para>

Les processeurs auxiliaires sont essentiellement des calculateurs dédiés 
à une tâche particulière, reliés à un système <emphasis>hôte</emphasis> 
et servant à accélérer certains types de calculs. Par exemple, de 
nombreuses cartes vidéo et son pour PC embarquent des processeurs dédiés 
conçus pour accélérer respectivement les opérations graphiques et le 
<emphasis>DSP</emphasis> audio (DSP&nbsp;: <quote><foreignphrase>Digital 
Signal Processing</foreignphrase></quote>, soit <quote>Traitement 
Numérique du Signal</quote>). Il existe aussi une large variété de 
<emphasis>processeurs de tableaux</emphasis> 
(<quote><foreignphrase>array processors</foreignphrase></quote>), nommés 
ainsi car ils sont conçus pour accélérer les opérations arithmétiques 
sur les tableaux. À dire vrai, un certain nombre de supercalculateurs 
commerciaux sont en réalité formés de processeurs dédiés rattachés à des 
stations de travail hôtes.

</para></listitem>
</varlistentry>

<varlistentry>
<term>RAID&nbsp;:</term>
<listitem><para>

RAID (<quote><foreignphrase>Redundant Array of Inexpensive 
Disks</foreignphrase></quote>, soit <quote>Batterie Redondante de 
Disques Peu Coûteux</quote>) est une technologie simple servant à 
améliorer tant la bande passante que la fiabilité des accès disque. Même 
si le RAID se décline en plusieurs variantes, toutes ont en commun deux 
concepts-clés&nbsp;: D'abord, chaque bloc de données est 
<emphasis>découpé</emphasis> en segments distribués à un groupe de 
<emphasis>n+k</emphasis> disques de façon à ce que chaque disque n'ait à 
lire que <emphasis>1/n</emphasis>ième de la donnée&hellip; offrant ainsi 
<emphasis>n</emphasis> fois la bande passante d'un seul disque. Ensuite, 
des données redondantes sont écrites pour que les données puissent être 
recouvrées si un des disques vient à défaillir. C'est important car 
autrement, si l'un des <emphasis>n+k</emphasis> disques tombait en 
panne, le système de fichiers entier pourrait être perdu. Il existe une 
bonne présentation du système RAID sur <ulink 
url="http://www.dpt.com/uraiddoc.html"/>, ainsi que des informations 
concernant le RAID pour Linux sur <ulink 
url="http://linas.org/linux/raid.html"/>. Hormis la prise en charge du 
matériel RAID spécialisé, Linux gère aussi le RAID logiciel 0, 1, 4 et 5 
à travers plusieurs disques hébergés sur un système Linux unique. 
Reportez-vous aux Software RAID mini-HOWTO et Multi-Disk System Tuning 
mini-HOWTO pour plus de détails. Le RAID au travers de plusieurs disques 
<emphasis>sur plusieurs machines en clusters</emphasis> n'est pas 
directement pris en charge.

</para></listitem>
</varlistentry>

<varlistentry>
<term>IA32&nbsp;:</term>
<listitem><para>

L'IA32 (<quote><foreignphrase>Intel 
Architecture</foreignphrase></quote>, 32 bits) n'a rien à voir avec le 
traitement en parallèle, mais se réfère à la classe de processeurs dont 
les instructions sont compatibles avec celles de l'Intel 386. 
Concrètement, tout processeur Intel x86 après le 286 est compatible avec 
le modèle de mémoire <quote>à&nbsp;plat<footnote><para>

N.D.T.&nbsp;: <quote><foreignphrase>flat memory 
model</foreignphrase></quote>, dans lequel toute la mémoire se trouve 
dans le même plan mémoire, par opposition aux segments, mémoire paginée, 
et tout autre modèle composé.

</para></footnote></quote> qui caractérise l'IA32. AMD et Cyrix font eux 
aussi une multitude de processeurs compatibles IA32. Comme Linux a 
évolué principalement sur des processeurs IA32 et que c'est là qu'est 
centré le marché de la grande consommation, il est commode d'utiliser le 
terme IA32 pour distinguer ce type de processeur des PowerPC, Alpha, 
PA-RISC, MIPS, SPARC, et cætera. La future IA64 (64 bits avec EPIC, 
<quote><foreignphrase>Explicitly Parallel Instruction 
Computing</foreignphrase></quote>) va certainement compliquer les 
choses, mais la production de Merced, le premier processeur IA64, n'est 
pas envisagée avant 1999.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Produits du commerce&nbsp;:</term>
<listitem><para>

Depuis la mort de plusieurs fabricants de supercalculateurs en parallèle,
les solutions commerciales toutes faites et prêtes à l'emploi (en 
anglais <quote><foreignphrase>Commercial Off-The-Shelf</foreignphrase></quote>
ou <foreignphrase>COTS</foreignphrase>, pour <quote>disponibles en 
rayons</quote>) sont couramment considérées comme une nécessité dans le 
monde des systèmes de calcul en parallèle. En étant totalement puriste, les 
seuls moyens de traitement en parallèle disponibles sous forme de 
produits du commerce utilisant des PC sont des choses comme les serveurs 
Windows NT en SMP et les différentes applications Windows utilisant le 
MMX. Être aussi puriste ne mène à rien. L'idée fondamentale de 
l'utilisation de produits du commerce est de réduire les coûts et les 
temps de développement. Ainsi, une manière plus complète et plus utile 
de comprendre l'utilisation de ce type de produit serait de dire que 
la plupart des sous-systèmes tirent profit du marché de masse mais que 
d'autres technologies sont utilisées là où elles servent vraiment. Le 
plus souvent, les produits du commerce pour le traitement en parallèle
sont utilisés au sein d'un groupe de machines 
(<quote><foreignphrase>cluster</foreignphrase></quote>) dans lequel les 
postes sont des PC courants, mais dont l'interface réseau et les 
logiciels ont été quelque peu <emphasis>personnalisés</emphasis>&hellip; 
classiquement, fonctionnant sous Linux avec des applications dont le 
code source est libre et disponible (par exemple sous
<foreignphrase>copyleft</foreignphrase> ou dans le domaine public), mais 
pas littéralement des produits du commerce.

</para></listitem>
</varlistentry>

</variablelist>

</sect2>

<sect2>
<title>Algorithme d'exemple</title>

<para>
Afin de bien comprendre l'usage des différentes approches de
programmation en parallèle mises en évidence dans ce guide,
il est utile d'étudier cet exemple. Bien qu'un algorithme
simple de traitement en parallèle eût suffi, si l'on en choisit
un qui a déjà été utilisé pour faire la démonstration d'autres
systèmes de programmation parallèle, il devient un peu plus facile
de comparer et mettre en évidence les caractéristiques de ces différentes approches. le livre
de M. J. Quinn, <foreignphrase>Parallel Computing Theory And Practice</foreignphrase>
(<quote>Théorie et Pratique du Calcul en Parallèle</quote>), seconde édition, édité
par McGraw Hill, New York en 1994, utilise un algorithme parallèle
qui calcule la valeur de Pi pour présenter différents environnements
de programmation sur supercalculateurs parallèles (par exemple, le
<foreignphrase>message passing</foreignphrase> du nCube ou la mémoire partagée des Sequent). Dans ce
guide, nous utiliserons le même algorithme.
</para>

<para>
Cet algorithme calcule la valeur approchée de Pi en faisant la
somme de l'aire située sous <emphasis>x</emphasis> au carré. En
tant que programme C purement séquentiel, l'algorithme ressemble à&nbsp;:
</para>

<para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;

main(int argc, char **argv)
{
  register double largeur, somme;
  register int intervalles, i;

  /* Lit le nombre d'intervalles désiré */
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;

  /* fait le calcul */
  somme = 0;
  for (i=0; i&#60;intervalles; ++i) {
    register double x = (i + 0.5) * largeur;
    somme += 4.0 / (1.0 + x * x);
  }
  somme *= largeur;

  printf("Estimation de la valeur de pi: %f\n", somme);

  return(0);
}
</programlisting>

</para>

<para>

En revanche, cet algorithme séquentiel conduit facilement à une 
implémentation <quote>parallèle et embarrassante</quote>. L'aire est 
subdivisée en intervalles, et un nombre quelconque de processeurs peut 
faire la somme de l'intervalle qui lui est assigné indépendamment des 
autres, sans nécessité d'interaction entre les processeurs. Une fois que 
les sommes locales ont toutes été calculées, elles sont additionnées 
pour former la somme globale. Cette étape requiert un certain niveau de 
coordination et de communication entre les différents processeurs. 
Enfin, cette somme globale est renvoyée à l'écran par un seul 
processeur, en tant que valeur approximative de Pi.

</para>

<para>
Dans ce guide, les différentes implémentations parallèles de cet algorithme
apparaissent là ou les différentes méthodes de programmation sont traitées.
</para>

</sect2>

<sect2>
<title>Structure du document</title>

<para>
Le reste de ce document est divisé en cinq parties. Les sections
2, 3, 4 et 5 correspondent aux trois différents types de configuration
matérielle pouvant assumer le traitement en parallèle en utilisant
Linux.
</para>

<itemizedlist>
<listitem><para>

La section 2 traite des systèmes Linux sur SMP, lesquels prennent
directement en charge l'exécution MIMD en utilisant la mémoire
partagée, même si les files de messages sont facilement mises en
place, elles aussi. Bien que Linux sache gérer les configurations
SMP jusqu'à 16 processeurs, la plupart des ordinateurs SMP de type PC
sont dotés soit de deux, soit de quatre processeurs identiques.

</para></listitem>

<listitem><para>

La section 3 traite des batteries d'ordinateurs en réseau 
(<quote><foreignphrase>clusters</foreignphrase></quote>), chaque machine 
fonctionnant sous Linux. Un <foreignphrase>cluster</foreignphrase> peut 
être utilisé comme un système de traitement en parallèle gérant 
directement l'exécution en MIMD et l'échange de messages, et peut-être 
même aussi la mémoire partagée logique. L'exécution SIMD simulée et la 
communication des fonctions d'agrégation peuvent aussi être prises en 
charge, selon le réseau exploité. Le nombre de processeurs compris dans 
un <foreignphrase>cluster</foreignphrase> peut s'étendre de deux à 
plusieurs milliers, la principale limitation étant le câblage physique 
du réseau. Dans certains cas, des machines de différents types peuvent 
être mélangées au sein d'un <foreignphrase>cluster</foreignphrase>. Par 
exemple, un réseau qui combinerait des Alpha DEC et des Pentium sous 
Linux serait appelé <foreignphrase>cluster</foreignphrase> 
<emphasis>hétérogène</emphasis>.

</para></listitem>

<listitem><para>

La section 4 traite du SWAR, le <quote>SIMD dans un registre</quote>. 
C'est une forme très restrictive d'exécution en parallèle, mais d'un 
autre coté, c'est une possibilité intégrée aux processeurs ordinaires. 
Ces derniers temps, les extensions MMX et autres des processeurs 
modernes ont rendu cette approche encore plus efficace.

</para></listitem>

<listitem><para>

La section 5 traite de l'utilisation de PC sous Linux comme hôtes pour 
des systèmes de calcul parallèle simples. Sous forme de carte 
d'extension ou de boîtiers externes, les processeurs auxiliaires peuvent 
apporter à des systèmes Linux une formidable puissance de traitement 
pour des applications spécifiques. Par exemple, des cartes ISA 
disponibles à peu de frais fournissent de multiples processeurs DSP, 
offrant plusieurs centaines de MégaFLOP aux calculs de grande envergure. 
En revanche, ces cartes ne sont <emphasis>que</emphasis> des 
processeurs. Elle n'embarquent généralement pas de système 
d'exploitation, de disque dur, ou de connecteur pour terminal de 
contrôle, et cætera. Pour rendre ces systèmes exploitables, 
l'<quote>hôte</quote> Linux doit fournir ces facilités.

</para></listitem>

</itemizedlist>

<para>

La section finale de ce document couvre les aspects d'intérêt général 
concernant le traitement en parallèle sous Linux, non spécifique à l'une 
des approches listées ci-dessus.

</para>

<para>

En lisant ce document, gardez à l'esprit que nous n'avons pas tout 
testé, et que beaucoup de choses rapportées dans ce document ont 
toujours <quote>un caractère expérimental</quote> (une jolie manière de 
dire que cela ne fonctionne pas tout à fait comme espéré ;-) ). Cela 
dit, le traitement en parallèle sous Linux est désormais exploitable, et 
un groupe incroyablement vaste de personnes travaille à le rendre encore 
meilleur.

</para>

<para>

L'auteur de la version originale de ce guide est le Dr (Ph.D) Hank 
Dietz, actuellement Professeur Associé de l'<foreignphrase>Electrical 
and Computer Engineering</foreignphrase> à l'université de Purdue, West 
Lafayette, IN, 47907-1285. Dietz est propriétaire des droits sur ce 
document, conformément aux règles du <foreignphrase>Linux Documentation 
Project</foreignphrase> (LDP). Bien qu'un effort ait été fait pour 
assurer l'exactitude de cette présentation, ni Dietz ni l'Université de 
Purdue ne peuvent être tenus responsables d'éventuels problèmes ou 
erreurs, et l'Université de Purdue n'endosse la responsabilité d'aucun 
produit ou travaux traités dans ce document.

</para>

</sect2>

<sect2>
<title>Note du traducteur</title>

<para>

Chers lecteurs, avant de poursuivre la lecture de ce guide, il est 
important de revenir, notament au vu de la date de publication de cette 
version française, sur plusieurs points&nbsp;:

</para>

<itemizedlist>

<listitem><para>

Le Professeur Henry G. Dietz (dit <quote>Hank</quote>), après avoir 
enseigné plusieurs années à l'Université de Purdue et y avoir développé 
la plupart de ce qui forme ce document, <emphasis>mène aujourd'hui ses recherches à 
l'Université du Kentucky</emphasis>. Son site personnel se trouve 
désormais ici: <ulink url="http://aggregate.org/hankd/"/>. Cela signifie 
également que la plupart des références à l'Université de Purdue sont 
désormais caduques. Toutefois, un certain nombre de ces références ont 
été conservées en l'état dans ce guide, ce lorsque le contenu référencé 
était toujours disponible sur le site de l'Université sans avoir été 
transféré vers le nouveau site. En tout état de cause, dirigez-vous en 
priorité sur le site de l'Université du Kentucky pour tout contact ou 
pour obtenir les informations les plus récentes.

</para></listitem>

<listitem><para>

La totalité des termes, notament techniques, employés dans ce documents 
ont été traduits en français, à quelques exceptions près. C'est par 
exemple le cas du mot 
<quote><foreignphrase>cluster</foreignphrase></quote>, qui désigne en 
informatique la mise en parallèle de plusieurs machines individuelles et 
coordonnées de manière à les faire agir comme un seul super-ordinateur. 
Le terme français homologue est <quote>grappe</quote>. Toutefois, la 
fréquence à laquelle ce mot est employé tant dans ce document (un 
chapitre entier est consacré à ce sujet précis) que dans la communauté 
du traitement en parallèle en général est telle que le terme original a 
été conservé dans la présente version française. Dans le même esprit, la 
notion de <quote>bande passante</quote> se retrouve très fréquement tout 
au long de ce guide. C'est à la base un abus de langage, mais la 
popularité de cette formule est également suffisament grande pour la 
conserver en l'état.

</para></listitem>

<listitem><para>

La version originale de ce document a été écrite en 1998, la version 
française est parue en 2004. Il va sans dire qu'au cours d'une aussi 
longue période, le paysage informatique a beaucoup évolué, spécialement 
en ce qui concerne le développement du noyau Linux. Certaines 
technologies réseau (telles que ATM, FireWire, ou Fiber Channel) ou de 
<foreignphrase>clustering</foreignphrase> (comme MOSIX), recensées comme 
indisponibles en 1998, ont depuis intégré le noyau, ou sont devenues 
disponibles. En revanche, il est très peu probable qu'une technologie 
connue pour fonctionner sous Linux lors de la rédaction de ce document 
soit devenue inutilisable depuis.

</para></listitem>

<listitem><para>

Plus encore que celui de l'industrie informatique, le paysage du 
<foreignphrase>World Wide Web</foreignphrase> s'est transformé de façon 
à rendre la plupart des liens proposés obsolètes. Un effort a été fait 
pour assurer leur mise à jour ou leur remplacement, ainsi que la 
pertinence de leur contenu. En dépit de cela, un certain nombre d'entre eux,
en particulier ceux dont les projets étaient hébergés sur les 
pages personnelles d'étudiants de grandes écoles, n'ont pu être corrigés 
et ont été retirés du document.

</para></listitem>

</itemizedlist>

<para>

Malgré toutes ces réserves, les techniques couvertes par ce document 
sont suffisament générales pour rester valables au cours du temps et au 
travers des différents modèles de machines, et son contenu présente 
toujours un intérêt à la fois pédagogique et historique, qui restera encore 
longtemps profitable au lecteur. Tout ceci justifie une publication même 
tardive.

</para>

<para>

Enfin, le traducteur s'est efforcé de rendre le présent document aussi 
correct et fidèle à son original que possible, mais n'est pas 
infaillible. Tout signalement d'un contresens, d'une erreur technique, 
ou tout autre défaut de traduction sera apprécié à sa juste valeur à 
l'adresse suivante&nbsp;:

<email>dvandenbroeck CHEZ free POINT fr</email>.

</para>

<para>

Bonne lecture&nbsp;!

</para>

</sect2>
</sect1>

<sect1>
<title>Linux sur SMP</title>

<para>

Ce document donne un bref aperçu de la manière dont on utilise <ulink 
url="http://www.linux.org.uk/SMP/title.html">le SMP sous Linux</ulink> 
pour le traitement en parallèle. L'information la plus à jour concernant 
le SMP sous Linux est fort probablement disponible via la liste de 
diffusion du SMP Linux Project (N.D.T.&nbsp;: en anglais). Envoyez un 
courrier électronique à

<email>majordomo CHEZ vger POINT rutgers POINT edu</email>

avec le texte <literal>subscribe linux-smp</literal> pour rejoindre la 
liste.

</para>

<para>

Le SMP sous Linux fonctionne-t-il vraiment&nbsp;? En juin 1996, j'ai 
fait l'achat d'un bi-Pentium 100MHz flambant neuf. Le système complet et 
assemblé, comprenant les deux processeurs, la carte-mère Asus, 256 
kilo-octets de mémoire cache, 32 méga-octets de RAM, le disque dur d'1.6 
giga-octet, le lecteur de CD-ROM 6X, une carte Stealth 64 et un moniteur 
15'' Acer m'a coûté 1800 dollars. Cela ne fait que quelques centaines de 
dollars de plus qu'un système monoprocesseur. Pour faire fonctionner le 
SMP sous Linux, il a suffi d'installer le Linux monoprocesseur 
d'origine, de recompiler le noyau en décommentant la ligne 
<literal>SMP=1</literal> dans le <emphasis>Makefile</emphasis> (bien que 
je trouve le fait de mettre <literal>SMP</literal> à 
<literal>1</literal> un peu ironique&nbsp;! ;-) ), et d'informer 
<literal>lilo</literal> de l'existence du nouveau noyau. Ce système 
présente une stabilité et des performances suffisamment bonnes pour 
qu'il me serve depuis de station de travail principale. Pour résumer, le 
SMP sous Linux, ça fonctionne&nbsp;!

</para>

<para>

La question qui se présente alors est&nbsp;: existe-t-il suffisamment 
d'API de haut niveau permettant d'écrire et d'exécuter des programmes en 
parallèle et utilisant la mémoire partagée sous Linux SMP&nbsp;? Courant 
1996, il n'y en avait pas beaucoup. Les choses ont changé. Par exemple, 
il existe désormais une bibliothèque POSIX de gestion des 
<foreignphrase>threads</foreignphrase><footnote> <para>

N.D.T.&nbsp;: décomposition d'un programme en plusieurs processus 
distincts, mais travaillant simultanément et de concert.

</para></footnote> très complète.

</para>

<para>

Bien que les performances soient moins élevées que celles des mécanismes 
de mémoire partagée natifs, un système Linux sur SMP peut aussi utiliser 
la plupart des logiciels de traitement en parallèle initialement 
développés pour des <foreignphrase>clusters</foreignphrase> de stations 
de travail en utilisant la communication par 
<foreignphrase>socket</foreignphrase>. Les <emphasis>sockets</emphasis> 
(voir section 3.3) fonctionnent à l'intérieur d'une machine en SMP, et 
même dans un <foreignphrase>cluster</foreignphrase> de machines SMP 
reliées en réseau. Cependant, les <foreignphrase>sockets</foreignphrase> 
engendrent beaucoup de pertes en temps inutiles pour du SMP. Cela 
complique le problème car Linux SMP n'autorise en général qu'un seul 
processeur à la fois à se trouver dans le noyau et le contrôleur 
d'interruption est réglé de façon à ce que seul le processeur de 
<foreignphrase>boot</foreignphrase><footnote><para>

N.D.T.&nbsp;: qui a démarré la machine avant de passer en mode SMP.

</para></footnote> puisse traiter les interruptions. En dépit de cela, 
l'électronique de communication typique des systèmes SMP est tellement 
meilleure que la plupart des <foreignphrase>clusters</foreignphrase> en 
réseau que les logiciels pour <foreignphrase>cluster</foreignphrase> 
fonctionneront souvent mieux sur du SMP que sur le 
<foreignphrase>cluster</foreignphrase> pour lequel ils ont été conçus.

</para>

<para>
Le reste de cette section traite de l'électronique contrôlant le SMP,
passe en revue les mécanismes Linux de base partageant de la mémoire
à travers les différents processus d'un programme en parallèle, fait
quelques remarques concernant l'atomicité, la volatilité, les verrous
et les lignes de cache, et donne enfin des références vers d'autres
ressources de traitement en parallèle à mémoire partagée.
</para>

<sect2>
<title>L'électronique SMP</title>

<para>

Bien que les systèmes SMP soit répandus depuis de nombreuses années, 
jusque très récemment, chaque machine tendait à implémenter les 
fonctions de base d'une manière suffisamment différente des autres pour 
que leur gestion par le système d'exploitation ne soit pas portable. Les 
choses ont changé avec la <foreignphrase>Intel's MultiProcessor 
Specification</foreignphrase> (Spécification MultiProcesseurs d'Intel) 
souvent désignée par <emphasis>MPS</emphasis>. La spécification MPS 1.4 
est actuellement disponible sous forme de document PDF sur <ulink 
url="http://www.intel.com/design/intarch/MANUALS/242016.htm"/><footnote><para>
N.D.T.&nbsp;: le lien vers l'ancienne version 1.1, lui, n'existe plus. La 
documentation la plus récente se trouve à ce jour sur <ulink 
url="http://www.intel.com/design/Pentium4/documentation.htm"/>.
</para></footnote>,
mais gardez à l'esprit qu'Intel réorganise souvent son site web. Un large 
panel de constructeurs fabrique des systèmes conformes à MPS pouvant 
recevoir jusqu'à quatre processeurs, mais en théorie, MPS admet bien 
plus de processeurs.

</para>

<para>

Les seuls systèmes non MPS et non IA32 reconnus par Linux SMP sont les 
machines SPARC multiprocesseurs de Sun4m. Linux SMP prend aussi en 
charge la plupart des machines Intel conformes à MPS 1.1 ou 1.4, 
comptant jusqu'à 16 processeurs 486DX, Pentium, Pentium MMX, Pentium Pro 
ou Pentium II. Parmi les processeurs IA32 non pris en charge (N.D.T.&nbsp;: 
par le SMP), on trouve les Intel 386 et 486SX/SLC (l'absence de 
coprocesseur mathématique interfère sur les mécanismes du SMP) et les 
processeurs AMD et Cyrix (qui nécessitent des circuits de gestion du SMP 
différents et qui ne semblent pas être disponibles à l'heure où ce 
document est écrit).

</para>

<para>
Il est important de bien comprendre que les performances de différents
systèmes conformes à MPS peuvent fortement varier. Comme l'on peut s'y
attendre, une des causes de différence de performance est la vitesse
du processeur&nbsp;: Une horloge plus rapide tend à rendre les systèmes plus
rapides, et un processeur Pentium Pro est plus rapide qu'un Pentium.
En revanche, MPS ne spécifie pas vraiment comment le matériel doit
mettre en &oelig;uvre la mémoire partagée, mais seulement comment cette
implémentation doit fonctionner d'un point de vue logiciel. Cela
signifie que les performances dépendent aussi de la façon dont
l'implémentation de la mémoire partagée interagit avec les caractéristiques
de Linux SMP et de vos applications en particulier.
</para>

<para>
La principale différence entre les systèmes conformes à MPS réside
dans la manière dont ils implémentent l'accès à la mémoire physiquement
partagée.
</para>

<sect3>
<title>Chaque processeur possède-t-il sa propre mémoire cache de niveau 2 (L2)&nbsp;?</title>

<para>
Certains systèmes MPS à base de Pentium, et tous les systèmes MPS
Pentium Pro et Pentium II ont des mémoires cache L2 indépendantes
(le cache L2 est embarqué dans le module des Pentium Pro et Pentium II).
Les mémoires caches L2 dissociées sont généralement réputées augmenter
les performances de l'ordinateur, mais les choses ne sont pas si évidentes
sous Linux. La principale complication provient du fait que l'ordonnanceur
de Linux SMP n'essaie pas de maintenir chaque processus sur
le même processeur, concept connu sous le nom d'<emphasis>affinité processeur</emphasis>.
Cela pourrait bientôt changer. Un débat a récemment eu lieu sur ce sujet dans la
communauté des développeurs Linux SMP, sous le titre <quote><foreignphrase>processor
bindings</foreignphrase></quote> (<quote>associations de processeurs</quote>). Sans affinité processeur,
des caches L2 séparés peuvent introduire des délais non négligeables
lorsqu'un processus se voit allouer une tranche de temps d'exécution
sur un processeur qui n'est pas le même que celui sur lequel il
s'exécutait juste avant.
</para>

<para>
Plusieurs systèmes relativement bon marché sont organisés de manière
à ce que deux processeurs Pentium puissent partager la même mémoire
cache L2. La mauvaise nouvelle, c'est que cela crée des conflits à
l'utilisation de ce cache, qui dégradent sérieusement les performances
lorsque plusieurs programmes séquentiels indépendants s'exécutent
simultanément. La bonne nouvelle, c'est que bon nombre de programmes
parallèles pourraient tirer profit de la mémoire cache partagée, car
si les deux processeurs veulent accéder à la même ligne de mémoire
partagée, seul un processeur doit aller la rapatrier dans le cache,
et l'on évite des conflits de bus. Le manque d'affinité processeur
peut aussi s'avérer moins désastreux avec un cache L2 partagé. Ainsi,
pour les programmes parallèles, il n'est pas vraiment certain que
partager la mémoire cache L2 soit si préjudiciable que l'on pourrait
le penser.
</para>

<para>
À l'usage, notre bi-Pentium à mémoire cache partagée
de 256Ko présente une vaste échelle de performances, dépendantes du
niveau d'activité noyau requis. Au pire, le gain en vitesse
n'atteint qu'un facteur de 1,2. En revanche, nous avons aussi
constaté une accélération de 2,1 fois la vitesse d'origine, ce qui
suggère que les calculs intensifs à base de SPMD tirent vraiment
profit de l'effet d'<quote>acquisition partagée</quote> (<quote><foreignphrase>shared fetch</foreignphrase></quote>).
</para>

</sect3>

<sect3>
<title>Configuration du bus&nbsp;?</title>

<para>
La première chose à dire est que la plupart des systèmes modernes
relient le processeur à un ou plusieurs bus PCI qui à leur tour
sont <quote>pontés</quote> vers un ou plusieurs bus ISA ou EISA. Ces ponts
engendrent des temps de latence, et l'ISA comme l'EISA offrent
généralement des bandes passantes plus réduites que le PCI
(ISA étant le plus lent). C'est pourquoi les disques, cartes vidéos et
autres périphériques de haute performance devraient en principe
être connectés sur un bus PCI.
</para>

<para>
Bien qu'un système MPS puisse apporter un gain en vitesse honorable
à plusieurs programmes parallèles de calcul intensif même avec un
seul bus PCI, les opérations d'entrées/sorties, elles, ne sont pas
meilleures que sur un système monoprocesseur. Elles sont peut-être même
un peu moins bonnes à cause des conflits de bus entre les processeurs.
Ainsi, si votre objectif est d'accélérer les entrées/sorties, prenez
soin de choisir un système MPS comportant plusieurs bus PCI indépendants
et plusieurs contrôleurs d'entrées/sorties (par exemple&nbsp;: plusieurs
chaînes SCSI). Il vous faudra être prudent, et sûr que Linux
reconnaît tout votre matériel. Gardez aussi à l'esprit le fait que
Linux n'autorise qu'un seul processeur à la fois à entrer en mode
noyau, aussi devrez-vous choisir des contrôleurs qui réduisent au
minimum le temps noyau nécessaire à leurs opérations. Pour atteindre
des performances vraiment très élevées, il se pourrait même qu'il
vous faille envisager d'effectuer vous-même les opérations d'entrée/sortie
de bas niveau directement depuis les processus utilisateurs,
sans appel système&hellip; ce n'est pas forcément aussi difficile que
cela en a l'air, et cela permet d'éviter de compromettre la sécurité du
système (voir la section 3.3 pour une description des techniques de
base).
</para>

<para>
Il est important de remarquer que la relation entre vitesse du bus
et vitesse du processeur est devenue très floue ces dernières années.
Bien que la plupart des systèmes utilisent maintenant la même fréquence
de bus PCI, il n'est pas rare de trouver un processeur rapide apparié
avec un bus lent. L'exemple classique est celui du Pentium 133 qui
utilise en général un bus plus rapide que celui du Pentium 150, produisant
des résultats étranges sur les logiciels bancs de tests (<quote><foreignphrase>benchmarks</foreignphrase></quote>).
Ces effets sont amplifiés sur les systèmes SMP, où il est encore plus
important d'utiliser un bus rapide.
</para>

</sect3>

<sect3>
<title>Interfoliage de la mémoire et technologie DRAM</title>

<para>
L'interfoliage de la mémoire n'a en fait absolument rien à voir
avec le MPS, mais vous verrez souvent cette mention accompagner
les systèmes MPS car ceux-ci sont typiquement gourmands en
bande passante mémoire. Concrètement, l'interfoliage en deux ou
en quatre voies organise la RAM de façon à ce que l'accès à un
bloc de mémoire se fasse au travers de plusieurs bancs de RAM
plutôt qu'un seul. Ceci accélère grandement les accès à la
mémoire, particulièrement en ce qui concerne le chargement et
l'enregistrement du contenu des lignes de cache.
</para>

<para>
Il faut toutefois souligner que ce fait n'est pas aussi évident
qu'il y parait, car la DRAM EDO et les différentes technologies
mémoire tendent à optimiser ce genre d'opérations. Un excellent
aperçu des différentes technologies DRAM est disponible sur
<ulink url="http://www.pcguide.com/ref/ram/tech.htm">http://www.pcguide.com/ref/ram/tech.htm</ulink>.
</para>

<para>
Ainsi, par exemple, mieux vaut-il avoir de la mémoire DRAM EDO
interfoliée à 2 voies, ou de la mémoire SDRAM non interfoliée&nbsp;?
C'est une très bonne question et la réponse n'est pas simple,
car la mémoire interfoliée comme les technologies DRAM exotiques
ont tendance à être coûteuses. Le même investissement en mémoire
plus ordinaire vous apporte en général une mémoire centrale bien
plus vaste. Même la plus lente des mémoire DRAM reste autrement
plus rapide que la mémoire virtuelle par fichier d'échange&hellip;
</para>

</sect3>

</sect2>

<sect2>
<title>Introduction à la programmation en mémoire partagée</title>

<para>
<foreignphrase>Okay</foreignphrase>, donc vous avez décidé que le traitement 
en parallèle sur SMP, c'est génial&hellip; Par quoi allez-vous commencer&nbsp;? Eh bien,
la première étape consiste à en apprendre un peu plus sur le fonctionnement réel
de la communication par mémoire partagée.
</para>

<para>
A première vue, il suffit qu'un processeur range une valeur en mémoire
et qu'un autre la lise. Malheureusement, ce n'est pas aussi simple.
Par exemple, les relations entre processus et processeurs sont très
floues. En revanche, si nous n'avons pas plus de processus actifs que
de processeurs, les termes sont à peu près interchangeables. Le reste
de cette section résume brièvement les cas de figure typiques qui
peuvent poser de sérieux problèmes, si vous ne les connaissiez pas
déjà&nbsp;: les deux différents modèles utilisés pour déterminer ce qui
est partagé, les problèmes d'atomicité, le concept de volatilité,
les instructions de verrouillage matériel, les effets de la ligne
de cache, et les problèmes posés par l'ordonnanceur de
Linux.
</para>

<sect3>
<title>Partage Intégral contre Partage Partiel</title>

<para>
Il existe deux modèles fondamentaux couramment utilisés en programmation
en mémoire partagée&nbsp;: le <emphasis>partage intégral</emphasis>
et le <emphasis>partage partiel</emphasis>. Ces modèles
permettent tous deux aux processeurs de communiquer en chargeant et
rangeant des données depuis et dans la mémoire. La différence réside
dans le fait que le partage intégral place toutes les structures en
mémoire partagée, quand le partage partiel, lui, distingue les structures
qui sont potentiellement partageables et celles qui sont
<emphasis>privées</emphasis>, propres à un seul processeur
(et oblige l'utilisateur à classer explicitement ses structures dans l'une
de ces catégories).
</para>

<para>
Alors quel modèle de partage mémoire faut-il utiliser&nbsp;? C'est surtout
une affaire de chapelle. Beaucoup de gens aiment le partage intégral
car ils n'ont pas spécialement besoin d'identifier les structures qui
doivent être partagées au moment de leur déclaration. On place simplement
des verrous sur les objets auxquels l'accès peut créer des conflits, pour
s'assurer qu'un seul processeur (ou processus) y accède à un moment donné.
Mais là encore, ce n'est pas aussi simple&hellip; aussi beaucoup d'autres
gens préfèrent, eux, le modèle relativement sûr du partage partiel.
</para>

<sect4>
<title>Partage intégral</title>

<para>
Le bon coté du partage intégral est que l'on peut aisément reprendre
un programme séquentiel existant et le convertir progressivement en
programme parallèle en partage intégral. Vous n'avez pas à déterminer
au préalable les données qui doivent être accessibles aux autres
processeurs.
</para>

<para>
Posé simplement, le principal problème avec le partage intégral vient
du fait qu'une action effectuée par un processeur peut affecter les
autres processeurs. Ce problème ressurgit de deux manières&nbsp;:
</para>

<para>

<itemizedlist>
<listitem>

<para>
Plusieurs bibliothèques utilisent des structures de données qui
ne sont tout simplement pas partageables. Par exemple, la convention
UNIX stipule que la plupart des fonctions peuvent renvoyer un code
d'erreur dans une variable appelée <literal>errno</literal>.
Si deux processus en partage intégral font des appels divers, ils vont
interférer l'un sur l'autre car ils partagent la même variable
<literal>errno</literal>. Bien qu'il existe désormais une
bibliothèque qui règle le problème de cette variable, ce problème se présente toujours
dans la plupart des bibliothèques comme par exemple X-Window qui, à moins de prendre
des précautions très spéciales, ne fonctionnera pas si différents appels sont
passés depuis différents processus en partage intégral.
</para>
</listitem>
<listitem>

<para>
En temps normal, un programme qui utilise un pointeur ou un index
défaillant provoque au pire l'arrêt du processus qui contient le code corrompu.
Il peut même générer un fichier <literal>core</literal> vous renseignant sur les conditions
dans lesquelles se sont déroulés les événements. En programmation parallèle
à partage intégral, il est fort probable que les accès illégaux provoquent
la <emphasis>fin d'un processus qui n'est pas le fautif</emphasis>, rendant
la localisation et la correction de l'erreur quasiment impossibles.
</para>
</listitem>

</itemizedlist>

</para>

<para>
Aucun de ces deux problèmes n'est courant dans le cas du partage
partiel, car seules sont partagées les structures explicitement marquées
comme telles. De plus, il est trivial que le partage intégral ne peut
fonctionner que si les processeurs exécutent exactement la même image
en mémoire. On ne peut pas utiliser le partage intégral entre des images
de code différentes (c'est-à-dire que vous pourrez travailler en SPMD,
mais pas d'une manière générale en MIMD).
</para>

<para>

Les supports de programmation en partage intégral existent le plus 
couramment sous la forme de <emphasis>bibliothèques de 
<foreignphrase>threads</foreignphrase></emphasis>. Les <ulink 
url="http://liinwww.ira.uka.de/bibliography/Os/threads.html"><foreignphrase>threads</foreignphrase></ulink> 
sont essentiellement des processus <quote>allégés</quote> dont 
l'exécution peut ne pas être planifiée comme celle des processus UNIX 
normaux et qui, c'est le plus important, partagent tous la même page 
mémoire. L'adaptation des <ulink 
url="http://www.mit.edu:8001/people/proven/IAP_2000/index.html">Pthreads</ulink> 
POSIX a fait l'objet de nombreux efforts. La grande question est&nbsp;: 
ces adaptations parallélisent-elles les 
<foreignphrase>threads</foreignphrase> d'un programme en environnement 
Linux SMP (idéalement, en attribuant un processeur à chaque 
<foreignphrase>thread</foreignphrase>)&nbsp;?. L'API POSIX ne l'impose 
pas, et certaines versions comme <emphasis>PCthreads</emphasis> semblent 
ne pas implémenter une exécution en parallèle des 
<foreignphrase>threads</foreignphrase>&nbsp;: tous les 
<foreignphrase>threads</foreignphrase> d'un programme sont conservés à 
l'intérieur d'un seul processus Linux.

</para>

<para>
La première bibliothèque de <foreignphrase>threads</foreignphrase> à avoir pris en charge le parallélisme
sous Linux SMP fut la désormais quelque peu obsolète bibliothèque
<emphasis>bb_thread</emphasis>, une toute petite bibliothèque qui utilisait l'appel Linux
<literal>clone()</literal> pour donner naissance à de nouveaux
processus Linux, planifiés indépendamment les uns des autres, tous partageant
un même espace d'adressage. Les machines Linux SMP peuvent lancer plusieurs
de ces <quote><foreignphrase>threads</foreignphrase></quote> car chaque <quote><foreignphrase>thread</foreignphrase></quote> est un processus Linux à part entière.
L'inconvénient, c'est que l'on ne peut obtenir l'ordonnancement <quote>poids-plume</quote>
apportée par les bibliothèques de <foreignphrase>threads</foreignphrase> d'autres systèmes
d'exploitation. La bibliothèque utilisait un peu de code assembleur intégré
dans un code source en langage C pour mettre en place un bloc de mémoire pour la
pile de chaque <foreignphrase>thread</foreignphrase> et fournir des fonctions d'accès atomiques à un tableau
de verrous (les <emphasis>mutex</emphasis>). Sa documentation se résumait à un fichier
<literal>LISEZMOI</literal> et à un court programme d'exemple.
</para>

<para>
Plus récemment, une version de <foreignphrase>threads</foreignphrase> POSIX utilisant <literal>clone()</literal>
a été développée. Cette bibliothèque,
<ulink url="http://pauillac.inria.fr/~xleroy/linuxthreads/">LinuxThreads</ulink>,
est clairement la bibliothèque en partage intégral favorite pour l'utilisation
sous Linux SMP. Les <foreignphrase>threads</foreignphrase> POSIX sont bien documentés, et les documents
<ulink url="http://pauillac.inria.fr/~xleroy/linuxthreads/README">LinuxThreads&nbsp;README</ulink>
et <ulink url="http://pauillac.inria.fr/~xleroy/linuxthreads/faq.html">LinuxThreads&nbsp;FAQ</ulink>
sont vraiment très bien réalisés. A présent, le principal problème est
que les <foreignphrase>threads</foreignphrase> POSIX ont encore beaucoup de détails à régler, et que
LinuxThread est toujours un projet en cours d'évolution. D'autre part,
les <foreignphrase>threads</foreignphrase> POSIX ont évolué pendant dans leur phase de standardisation,
aussi devrez-vous être prudent pour ne pas développer en suivant une version obsolète du standard.
</para>

</sect4>

<sect4>
<title>Partage Partiel</title>

<para>

Le Partage Partiel consiste réellement à <quote>ne partager que ce qui 
doit être partagé</quote>. Cette approche est valable pour le MIMD en 
général (et pas simplement le SPMD) à condition de prendre soin 
d'allouer les objets partagés aux mêmes endroits dans le plan mémoire de 
chaque processeur. Plus important encore, le partage partiel facilite 
l'estimation et l'ajustage des performances, le débogage des sources, et 
cætera. Les seuls problèmes sont&nbsp;:

</para>

<para>

<itemizedlist>
<listitem>

<para>
Déterminer à l'avance ce qui doit être partagé peut s'avérer difficile.
</para>
</listitem>
<listitem>

<para>
L'allocation d'objets dans la mémoire partagée peut en fait se révéler
malaisé, spécialement en ce qui concerne tout ce qui aurait du être déclaré
dans la pile. Par exemple, il peut être nécessaire d'allouer explicitement
des objets partagés dans des segments de mémoire séparés, nécessitant des
routines d'allocation mémoire séparées, et impliquant l'ajout de pointeurs
et d'indirections supplémentaires à chaque référence.
</para>
</listitem>

</itemizedlist>

</para>

<para>

Actuellement, il existe deux mécanismes similaires permettant aux 
groupes de processus sous Linux de posséder des espaces mémoire 
indépendants, mais de tous partager un unique et relativement étroit 
segment de mémoire. En supposant que vous n'ayez pas bêtement exclu 
l'option <quote>System V IPC</quote> lorsque que vous avez configuré 
votre système Linux (N.D.T.&nbsp;: ici à la recompilation du noyau), Linux 
gère un mécanisme très portable devenu célèbre sous le nom de 
<quote>mémoire partagée System V</quote>. L'autre alternative est une 
fonction de projection en mémoire dont l'implémentation varie grandement 
selon le système UNIX utilisé&nbsp;: L'appel système 
<literal>mmap</literal>. Vous pouvez &mdash; et devriez &mdash; 
apprendre le fonctionnement de ces primitives au travers des pages du 
manuel (<foreignphrase>man pages</foreignphrase>)&hellip; mais vous 
trouverez quand même un rapide aperçu de chacune d'elles dans les 
sections 2.5 et 2.6 pour vous aider à démarrer.

</para>

</sect4>

</sect3>

<sect3>
<title>Atomicité et ordonnancement</title>

<para>
Que vous utilisiez l'un ou l'autre des modèles cités ci-dessus, le
résultat est à peu près le même&nbsp;: vous obtenez un pointeur sur un bloc de
mémoire en lecture/écriture accessible par tous les processus de votre
programme en parallèle. Cela signifie-t-il que je peux laisser mes
programmes accéder aux objets partagés comme s'ils se trouvaient en
mémoire locale ordinaire&nbsp;? Pas tout à fait&hellip;
</para>

<para>
L'<emphasis>atomicité</emphasis> désigne une opération sur
un objet effectuée en une séquence indivisible et ininterruptible.
Malheureusement, les accès à la mémoire partagée n'impliquent pas que
les toutes les opérations sur les données de cette mémoire se fassent
de manière atomique. A moins de prendre des précautions spéciales, seules les
opérations de lecture ou d'écriture s'accomplissant en une seule transaction
sur le bus (c'est-à-dire alignées sur une adresse multiple de 8, 16 ou 32
bits, à l'exclusion des opérations 64 bits ou mal alignées) sont atomiques. Pire encore,
les compilateurs <quote>intelligents</quote> comme GCC font souvent des optimisations qui
peuvent éliminer les opérations mémoire nécessaires pour s'assurer que les autres processeurs
puissent voir ce que le processeur concerné a fait. Heureusement, ces problèmes ont tous
deux une solution&hellip; en acceptant seulement de ne pas se soucier de la
relation entre l'efficacité des accès mémoire et la taille de la ligne de
cache.
</para>

<para>
En revanche, avant de traiter de ces différents cas de figure, il est
utile de préciser que tout ceci part du principe que les références à
la mémoire pour chaque processeur se produisent dans l'ordre où elles
ont été programmées. Le Pentium fonctionne de cette manière, mais les
futurs processeurs d'Intel pourraient ne pas le faire. Aussi, quand
vous développerez sur les processeurs à venir, gardez à l'esprit qu'il
pourrait être nécessaire d'encadrer les accès à la mémoire avec des
instructions provoquant l'achèvement de toutes les accès à la mémoire
en suspens, provoquant ainsi leur mise en ordre.
L'instruction <literal>CPUID</literal> semble provoquer
cet effet.
</para>

</sect3>

<sect3>
<title>Volatilité</title>

<para>
Pour éviter que l'optimiseur du GCC ne conserve les valeurs de la
mémoire partagée dans les registres de processeur, tous les objets
en mémoire partagée doivent être déclarés avec l'attribut
<literal>volatile</literal>. Tous les accès en lecture ou écriture
ne nécessitant l'accès qu'à un seul mot se feront alors de manière
atomique. Par exemple, en supposant que <emphasis>p</emphasis> est un pointeur
sur un entier, et que ce pointeur comme l'entier qu'il pointe se trouvent en
mémoire partagée, la déclaration en C ANSI ressemblera à&nbsp;:
</para>

<para>

<programlisting>
volatile int * volatile p;
</programlisting>

</para>

<para>

Dans ce code, le premier <literal>volatile</literal> concerne 
l'<literal>int</literal> que <literal>p</literal> pointe 
éventuellement, quand le second <literal>volatile</literal> s'applique 
au pointeur lui-même. Oui, c'est ennuyeux, mais c'est le prix à payer 
pour que GCC puisse faire des optimisations vraiment puissantes. En 
théorie, l'option <literal>-traditional</literal> devrait suffire à 
produire du code correct au prix de quelques optimisations, car le 
standard C K&amp;R (N.D.T.&nbsp;: Kernigan &amp; Ritchie) pré norme ANSI 
établit que toutes les variables sont volatiles si elles ne sont pas 
explicitement déclarées comme <literal>register</literal>. Ceci étant 
dit, si vos compilations GCC ressemblent à <literal>cc -O6 
&hellip;</literal>, vous n'aurez réellement besoin de déclarer les 
choses comme étant volatiles qu'aux endroits où c'est nécessaire.

</para>

<para>
Un rumeur a circulé à propos du fait que les verrous écrits en assembleur
signalés comme modifiant tous les registres du processeur provoquaient de
la part du compilateur GCC l'enregistrement adéquat de toutes les variables en
suspens, évitant ainsi le code compilé <quote>inutile</quote> associé aux objets déclarés
<literal>volatile</literal>. Cette astuce semble fonctionner pour
les variables globales statiques avec la version 2.7.0 de GCC&hellip; En revanche,
ce comportement n'est <emphasis>pas</emphasis> une recommandation du standard
C ANSI. Pire encore, d'autres processus n'effectuant que des accès en lecture
pourraient conserver éternellement les valeurs dans des registres, et ainsi
ne <emphasis>jamais</emphasis> s'apercevoir que la vraie valeur stockée en
mémoire partagée a en fait changé. En résumé, développez comme vous l'entendez,
mais seules les variables déclarées <literal>volatile</literal>
offrent un fonctionnement normal <emphasis>garanti</emphasis>.
</para>

<para>
Notez qu'il est possible de provoquer un accès volatile à une variable
ordinaire en utilisant un transtypage (<quote><emphasis>casting</emphasis></quote>) imposant l'attribut
<literal>volatile</literal>. Par exemple, un
<literal>int i;</literal> ordinaire peut être référencé en tant
que volatile par <literal>*((volatile int *) &amp;i);</literal> .
Ainsi, vous pouvez forcer la volatilité et les coûts supplémentaires qu'elle engendre
seulement aux endroits où elle est critique.
</para>

</sect3>

<sect3>
<title>Verrous (<foreignphrase>Locks</foreignphrase>)</title>

<para>

Si vous pensiez que <literal>++i;</literal> aurait toujours incrémenté 
une variable <literal>i</literal> sans problème, vous allez avoir une 
mauvaise surprise&nbsp;: même codées en une seule instruction, le 
chargement et l'enregistrement du résultat sont deux transactions 
mémoire séparées, et d'autres processeurs peuvent accéder à 
<literal>i</literal> entre ces deux transactions. Par exemple, deux 
processus effectuant chacun l'instruction <literal>++i;</literal> 
pourraient n'incrémenter la variable <literal>i</literal> que d'une 
unité et non deux. Selon le <quote>Manuel de l'Architecture et de la 
Programmation</quote> du Pentium d'Intel, le préfixe 
<literal>LOCK</literal> peut être employé pour s'assurer que chacune des 
instructions suivantes soit atomique par rapport à l'adresse mémoire à 
laquelle elles accèdent&nbsp;:

</para>

<para>

<programlisting>
BTS, BTR, BTC                     mem, reg/imm
XCHG                              reg, mem
XCHG                              mem, reg
ADD, OR, ADC, SBB, AND, SUB, XOR  mem, reg/imm
NOT, NEG, INC, DEC                mem
CMPXCHG, XADD
</programlisting>

</para>

<para>
En revanche, il n'est pas conseillé d'utiliser toutes ces opérations.
Par exemple, <literal>XADD</literal> n'existait même pas sur 386,
aussi l'employer en programmation peut poser des problèmes de portabilité.
</para>

<para>

L'instruction <literal>XCHG</literal> engendre <emphasis>toujours</emphasis>
un verrou, même sans le préfixe <literal>LOCK</literal>, et est ainsi
et indiscutablement l'opération atomique favorite pour construire d'autres opérations
atomiques de plus haut niveau comme les sémaphores et les files d'attente partagées.
Bien sûr, on ne peut pas demander à GCC de générer cette instruction en écrivant
simplement du code C. Il vous faudra à la place écrire un peu de code assembleur en
ligne<footnote><para>

N.D.T.&nbsp;: inséré au sein du code source, ici en C.

</para></footnote>. En prenant un objet volatile 
<emphasis>obj</emphasis> et un registre du processeur 
<emphasis>reg</emphasis>, tous deux de type <literal>word</literal> 
(longs de 16 bits), le code assembleur GCC sera&nbsp;:

</para>

<programlisting>
__asm__ __volatile__ ("xchgl %1,%0"
                      :"=r" (reg), "=m" (obj)
                      :"r" (reg), "m" (obj));
</programlisting>

<para>

Quelques exemples de programmes assembleur en ligne utilisant des 
opérations bit-à-bit pour réaliser des verrous sont disponibles dans le 
code source de la bibliothèque bb_threads.

</para>

<para>

Il est toutefois important de se souvenir que faire des transactions 
mémoire atomiques a un coût. Une opération de verrouillage engendre des 
délais supplémentaires assez importants et peut retarder l'activité 
mémoire d'autres processeurs, quand des références ordinaires auraient 
utilisé le cache local. Les meilleures performances s'obtiennent en 
utilisant les opérations atomiques aussi <emphasis>peu</emphasis> 
souvent que possible. De plus, ces instructions atomiques IA32 ne sont 
évidement pas portables vers d'autres systèmes.

</para>

<para>

Il existe plusieurs alternatives permettant aux instructions ordinaires 
d'être utilisées pour mettre en &oelig;uvre différents types de 
synchronisation, y compris l'<emphasis>exclusion mutuelle</emphasis>, 
qui garantit qu'au plus un seul processeur met à jour un objet partagé 
donné à un moment précis. La plupart des manuels des différents systèmes 
d'exploitation traitent d'au moins une de ces techniques. On trouve un 
très bon exposé sur le sujet dans la quatrième édition des 
<foreignphrase>Operating System Concepts</foreignphrase> (Principes des 
Systèmes d'Exploitation), par Abraham Silberschatz et Peter B. Galvin, 
ISBN 0-201-50480-4.

</para>

</sect3>

<sect3>
<title>Taille de la ligne de cache</title>

<para>

Encore une chose fondamentale concernant l'atomicité et qui peut avoir 
des conséquence dramatiques sur les performances d'un SMP&nbsp;: la 
taille de la ligne de cache. Même si le standard MPS impose que les 
références soient cohérentes quelque soit le cache utilisé, il n'en 
reste pas moins que lorsque qu'un processeur écrit sur une ligne 
particulière de la mémoire, chaque copie en cache de l'ancienne ligne 
doit être invalidée ou mise à jour. Ceci implique que si au moins deux 
processeurs écrivent chacun sur des portions différentes de la ligne de 
cache, cela peut provoquer un trafic important sur le bus et le cache, 
pour au final transférer la ligne depuis le cache vers le cache. Ce 
problème est connu sous le nom de <emphasis>faux partage</emphasis> 
(<quote><foreignphrase>false sharing</foreignphrase></quote>). La 
solution consiste uniquement à <emphasis>organiser les données de telle 
manière que ce que les objets auxquels on accède en parallèle 
proviennent globalement de différentes lignes de cache pour chaque 
processus</emphasis>.

</para>

<para>

Vous pourriez penser que le faux partage n'est pas un problème quand on 
utilise un cache de niveau 2 partagé, mais souvenez-vous qu'il existe 
toujours des caches de niveau 1 séparés. L'organisation du cache et le 
nombre de niveaux séparés peut varier, mais la ligne de cache de premier 
niveau d'un Pentium est longue de 32 octets, et le cache externe typique 
tourne autour de 256 octets. Supposons que les adresses (physiques ou 
logiques) de deux objets soient <emphasis>a</emphasis> et 
<emphasis>b</emphasis>, et que la taille de la ligne de cache soit 
<emphasis>c</emphasis>, que nous admettrons être une puissance de 2. 
Pour être très précis, si

<literal>((int) a) &amp; &tilde;(c-1)</literal> est égal à 
<literal>((int) b) &amp; &tilde;(c-1)</literal>,

alors les deux références se trouvent dans la même ligne de cache. Une 
règle plus simple consiste à dire que si deux objets référencés en 
parallèle sont éloignés d'au moins <emphasis>c</emphasis> octets, ils 
devraient se trouver dans des lignes de cache différentes.

</para>

</sect3>

<sect3>
<title>Les problèmes de l'ordonnanceur de Linux</title>

<para>

Bien que tout l'intérêt d'utiliser de la mémoire partagée pour les 
traitements en parallèle consiste à éviter les délais dus au système 
d'exploitation, ces délais peuvent parfois provenir d'autres choses que 
les communications en elles-mêmes. Nous avons déjà remarqué que le 
nombre de processus que l'on devrait créer doit être inférieur ou égal 
au nombre de processeurs de la machine. Mais comment décide-t-on 
exactement du nombre de processus à créer&nbsp;?

</para>

<para>

Pour obtenir les meilleures performances, <emphasis>le nombre de 
processus de votre programme en parallèle doit être égal au nombre de 
processus qui peuvent être exécutés simultanément, chacun sur son 
processeur</emphasis>. Par exemple, si un système SMP à quatre 
processeurs héberge un processus très actif pour un autre usage (par 
exemple un serveur <foreignphrase>web</foreignphrase>), alors votre 
programme en parallèle ne devra utiliser que trois processus. Vous 
pouvez vous faire une idée générale du nombre de processus actifs 
exécutés sur votre système en consultant la <quote>charge 
système moyenne</quote> (<quote><foreignphrase>load 
average</foreignphrase></quote>) mise en évidence par la commande 
<literal>uptime</literal>.

</para>

<para>

Vous pouvez en outre <quote>pousser</quote> la priorité de vos processus 
de votre programme parallèle en utilisant, par exemple, la commande 
<literal>renice</literal> ou l'appel système <literal>nice()</literal>. 
Vous devez être privilégié<footnote><para>
 
N.D.T.&nbsp;: soit, sous Unix, être sous le compte <literal>root</literal>.

</para></footnote> pour augmenter la priorité d'un processus. L'idée 
consiste simplement à éjecter les autres programmes des autres 
processeurs pour que votre programme puisse être exécuté sur tous les 
processeurs simultanément. Ceci peut être effectué de manière un peu 
plus explicite en utilisant la version prototype de Linux SMP disponible 
sur <ulink url="http://www.fsmlabs.com/products/openrtlinux/"/> et qui 
propose un ordonnanceur en temps réel (N.D.T.&nbsp;: il existe désormais un 
guide consacré à RTLinux, accessible en ligne&nbsp;: <ulink 
url="&howto;RTLinux-HOWTO.html">RTLinux 
HOWTO</ulink>).

</para>

<para>

Si vous n'êtes pas le seul utilisateur employant votre système SMP comme 
une machine en parallèle, il se peut que vous entriez en conflit avec 
les autres programmes en parallèle essayant de s'exécuter simultanément. 
La solution standard est l'<emphasis>ordonnancement de groupe</emphasis> 
(<quote><foreignphrase>gang scheduling</foreignphrase></quote>), 
c'est-à-dire la manipulation de la priorité d'ordonnancement de façon à 
ce que seuls les processus d'un seul programme en parallèle s'exécutent 
à un moment donné. Il est bon de rappeler, en revanche, que multiplier 
les parallélismes tend à réduire les retours et que l'activité de 
l'ordonnanceur introduit des délais supplémentaires. Ainsi, par exemple, 
il sera sûrement préférable, pour une machine à quatre processeurs, 
d'exécuter deux programmes contenant chacun deux processus, plutôt que 
d'ordonnancer en groupe deux programmes de quatre processus chacun.

</para>

<para>

Il y a encore une chose dont il faut tenir compte. Supposons que vous 
développiez un programme sur une machine très sollicitée le jour, mais 
disponible à cent pour cent pendant la nuit pour le traitement en 
parallèle. Il vous faudra écrire et tester votre code dans les 
conditions réelles, donc avec tous ses processus lancés, même en sachant 
que des tests de jour risquent d'être lents. Ils seront en fait 
<emphasis>très</emphasis> lents si certains de vos processus sont en 
état d'<emphasis>attente active</emphasis> (<quote><foreignphrase>busy 
waiting</foreignphrase></quote>)<footnote><para>

N.D.T.&nbsp;: temporisations introduites au sein d'un programme en 
utilisant par exemple des boucles et en consommant ainsi tout le temps 
machine alloué au processus plutôt qu'en rendant la main au système.

</para></footnote>, guettant le changement de certaines valeurs en 
mémoire partagée, changement censé être provoqué par d'autres processus 
qui ne sont pas exécutés (sur d'autres processeurs) au même moment. Ce 
même problème apparaît lorsque l'on développe et que l'on teste un 
programme sur un système monoprocesseur.

</para>

<para>

La solution consiste à intégrer des appels système à votre code là où il 
peut se mettre en boucle en attendant une action d'un autre processeur, 
pour que Linux puisse donner une chance de s'exécuter à un autre 
processus. J'utilise pour cela une macro en langage C, appelons-la 
<literal>IDLE_ME</literal> (N.D.T.&nbsp;: 
<literal>MetsMoiEnAttente</literal>)&nbsp;: pour faire un simple test, 
compilez votre programme par

<quote><literal>cc -DIDLE_ME=usleep(1);&hellip;</literal></quote>.

Pour produire un exécutable définitif, utilisez

<quote><literal>cc -DIDLE_ME={}&hellip;</literal></quote>.

L'appel <literal>usleep(1)</literal> réclame une pause d'une 
microseconde, qui a pour effet de permettre à l'ordonnanceur de Linux de 
choisir un nouveau processus à exécuter sur ce processeur. Si le nombre 
de processus dépasse le nombre de processeurs disponibles, il n'est pas 
rare de voir des programmes s'exécuter dix fois plus rapidement avec 
<literal>usleep(1)</literal> que sans.

</para>

</sect3>

</sect2>

<sect2>
<title>bb_threads</title>

<para>

La bibliothèque bb_threads (<foreignphrase>"Bare Bones" 
threads</foreignphrase>) est une bibliothèque remarquablement simple qui 
fait la démonstration de l'utilisation de l'appel système Linux 
<literal>clone()</literal>. Le fichier <literal>tar.gz</literal> 
n'occupe que 7&nbsp;ko&nbsp;! Bien que cette bibliothèque ait été rendue 
pour l'essentiel obsolète par la bibliothèque LinuxThreads, traitée dans 
la section 2.4, bb_threads reste utilisable, et est suffisamment simple 
et peu encombrante pour former une bonne introduction à la gestion des 
<foreignphrase>threads</foreignphrase> sous Linux. Il est beaucoup moins 
effrayant de se lancer dans la lecture de ce code source que dans celui 
de LinuxThreads. En résumé, la bibliothèque bb_threads forme un bon 
point de départ, mais n'est pas vraiment adaptée à la réalisation de 
grands projets.

</para>

<para>

La structure de base des programmes utilisant la bibliothèque bb_threads 
est la suivante&nbsp;:

</para>

<orderedlist>

<listitem><para>

Lancez le programme en tant que processus unique.

</para></listitem>

<listitem><para>

Il vous faudra estimer l'espace maximum dans la pile qui sera nécessaire 
à chaque <foreignphrase>thread</foreignphrase>. Prévoir large est 
relativement sage (c'est à çà que sert la mémoire virtuelle ;-), mais 
souvenez-vous que <emphasis>toutes</emphasis> les piles proviennent d'un 
seul espace d'adressage virtuel, aussi voir trop grand n'est pas une 
idée formidable. La démo suggère 64Ko. Cette taille est fixée à 
<emphasis>b</emphasis> octets par 
<literal>bb_threads_stacksize(b)</literal>.

</para></listitem>

<listitem><para>

L'étape suivante consiste à initialiser tous les verrous dont vous aurez 
besoin. Le mécanisme de verrouillage intégré à cette bibliothèque 
numérote les verrous de 0 à <literal>MAX_MUTEXES</literal>, et 
initialise un verrou <emphasis>i</emphasis> par 
<literal>bb_threads_mutexcreate(i)</literal>.

</para></listitem>

<listitem><para>

La création d'un nouveau <foreignphrase>thread</foreignphrase> 
s'effectue en appelant une routine de la bibliothèque recevant en 
arguments la fonction que le nouveau 
<foreignphrase>thread</foreignphrase> doit exécuter, et les arguments 
qui doivent lui être transmis. Pour démarrer un nouveau 
<foreignphrase>thread</foreignphrase> exécutant la fonction 
<emphasis>f</emphasis> de type <literal>void</literal> et attendant un 
argument <emphasis>arg</emphasis>, l'appel ressemblera à 
<literal>bb_threads_newthread (f, &amp;arg)</literal>, où 
<emphasis>f</emphasis> devra être déclaré comme suit&nbsp;:

</para>

<programlisting>
void f (void *arg, size_t dummy)
</programlisting>

<para>

Si vous avez besoin de passer plus d'un argument à votre fonction, 
utilisez un pointeur sur une structure contenant les valeurs à 
transmettre.

</para></listitem>

<listitem><para>

Lancement du code en parallèle, en prenant soin d'utiliser 
<literal>bb_threads_lock(n)</literal> et 
<literal>bb_threads_unlock(n)</literal> où <emphasis>n</emphasis> est un 
entier indiquant le verrou à utiliser. Notez que les opérations de 
verrouillage et déverrouillage sont des opérations de blocage<footnote><para>

N.D.T.&nbsp;: <quote><foreignphrase>spin 
locks</foreignphrase></quote>&nbsp;: mise en état d'attente jusqu'à ce 
qu'une condition soit remplie.

</para></footnote> très primaires et utilisant des instructions 
atomiques de verrouillage du bus, lesquelles peuvent causer des 
interférences d'accès à la mémoire, et qui n'essaient en aucun cas 
d'agir <quote>proprement</quote>.

Le programme de démonstration fourni avec bb_threads n'utilisait pas 
correctement les verrous pour empêcher <literal>printf()</literal> 
d'être exécuté depuis les fonctions <literal>fnn</literal> et 
<literal>main</literal>, et à cause de cela, la démo ne fonctionne pas 
toujours. Je ne dis pas cela pour démolir la démo, mais plutôt pour bien 
mettre en évidence le fait que ce travail comporte <emphasis>beaucoup de 
pièges</emphasis>. Ceci dit, utiliser LinuxThreads ne se révèle que 
légèrement plus facile.

</para></listitem>

<listitem><para>

Lorsqu'un <foreignphrase>thread</foreignphrase> exécute 
<literal>return</literal>, il détruit le processus&hellip; mais la pile 
locale n'est pas automatiquement désallouée. Pour être plus précis, 
Linux ne gère pas la désallocation, et l'espace mémoire n'est pas 
automatiquement rendu à la liste d'espace libre de 
<literal>malloc()</literal>. Aussi, le processus parent doit-il 
récupérer l'espace mémoire de chaque processus fils mort par 
<literal>bb_threads_cleanup(wait(NULL))</literal>.

</para></listitem>

</orderedlist>

<para>

Le programme suivant, écrit en langage C, utilise l'algorithme traité 
dans la section 1.3 pour calculer la valeur de Pi en utilisant deux 
<foreignphrase>threads</foreignphrase> bb_threads.

</para>

<programlisting>
#include &#60;stdio.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;unistd.h&#62;
#include &#60;sys/types.h&#62;
#include &#60;sys/wait.h&#62;
#include "bb_threads.h"

volatile double pi = 0.0;
volatile int intervalles;
volatile int pids[2];      /* Numéros de processus Unix des threads */

void
do_pi(void *data, size_t len)
{
  register double largeur, sommelocale;
  register int i;
  register int iproc = (getpid() != pids[0]);

  /* Fixe la largeur des intervalles */
  largeur = 1.0 / intervalles;

  /* Effectue les calculs locaux */
  sommelocale = 0;
  for (i=iproc; i&#60;intervalles; i+=2) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;

  /* Obtention des permissions, mise à jour de Pi, et déverrouillage */
  bb_threads_lock(0);
  pi += sommelocale;
  bb_threads_unlock(0);
}

int
main(int argc, char **argv)
{
  /* Récupère le nombre d'intervalles */
  intervalles = atoi(argv[1]);

  /* Fixe la taille de la pile, et crée le verrou */
  bb_threads_stacksize(65536);
  bb_threads_mutexcreate(0);

  /* crée deux threads ... */
  pids[0] = bb_threads_newthread(do_pi, NULL);
  pids[1] = bb_threads_newthread(do_pi, NULL);

  /* nettoie derrière les deux threads */
  /* (forme ainsi une barrière de synchro) */

  bb_threads_cleanup(wait(NULL));
  bb_threads_cleanup(wait(NULL));

  /* Affiche le résultat */
  printf("Estimation de la valeur de Pi: %f\n", pi);

  /* Sortie avec code de SUCCES */
  exit(0);
}
</programlisting>

</sect2>

<sect2>
<title>LinuxThreads</title>

<para>

LinuxThreads (<ulink 
url="http://pauillac.inria.fr/~xleroy/linuxthreads/"/>) est une 
implémentation assez complète et bien construite en accord avec le 
standard de <foreignphrase>threads</foreignphrase> POSIX 1003.1c. 
Contrairement aux autres adaptations d'implémentations de 
<foreignphrase>threads</foreignphrase> POSIX, LinuxThreads utilise 
également l'appel <literal>clone()</literal> du noyau Linux, déjà 
employé par bb_threads. La compatibilité POSIX implique qu'il est 
relativement aisé de faire l'adaptation de certaines applications 
provenant d'autres systèmes, et différents tutoriels et leur support 
sont disponibles. Bref, c'est incontestablement la bibliothèque à 
utiliser pour développer des applications 
<foreignphrase>multi-threads</foreignphrase> à grande échelle sous 
Linux.

</para>

<para>

La structure de base d'un programme utilisant LinuxThreads suit ce 
modèle&nbsp;:

</para>

<orderedlist>
<listitem><para>

Lancement du programme en tant que processus unique.

</para></listitem>

<listitem><para>

Initialisation de tous les verrous dont vous aurez besoin.
Contrairement aux verrous de bb_threads qui sont identifiés par
des numéros, les verrous POSIX sont déclarés comme des variables de
type <literal>pthread_mutex_t lock</literal>.
Utilisez <literal>pthread_mutex_init(&amp;lock,val)</literal>
pour initialiser chacun des verrous que vous utiliserez.

</para></listitem>

<listitem><para>

Comme avec bb_threads, la création d'un nouveau 
<foreignphrase>thread</foreignphrase> se fait par l'appel d'une fonction 
de la bibliothèque admettant des arguments spécifiant à leur tour la 
fonction que le nouveau <foreignphrase>thread</foreignphrase> doit 
exécuter et les arguments que celle-ci reçoit. Cependant, POSIX impose à 
l'utilisateur la déclaration d'une variable de type 
<literal>pthread_t</literal> pour identifier chaque 
<foreignphrase>thread</foreignphrase>. Pour créer un 
<foreignphrase>thread</foreignphrase> <literal>pthread_t 
thread</literal> exécutant la fonction <literal>f()</literal>, on 
appelle <literal>pthread_create(&amp;thread,NULL,f,&amp;arg)</literal>.

</para></listitem>

<listitem><para>

Lancement de la partie parallèle du programme, en prenant soin d'utiliser

<literal>pthread_mutex_lock(&amp;lock)</literal> et

<literal>pthread_mutex_unlock(&amp;lock)</literal>

comme il se doit.

</para></listitem>

<listitem><para>

Utilisation de <literal>pthread_join(thread,&amp;retval)</literal>
après chaque <foreignphrase>thread</foreignphrase> pour tout nettoyer.

</para></listitem>

<listitem><para>

Utilisation de <literal>-D_REENTRANT</literal> à la compilation de votre 
programme en C.

</para></listitem>

</orderedlist>

<para>

Voici l'exemple du calcul de Pi en parallèle, s'appuyant sur 
LinuxThreads. L'algorithme de la section 1.3 est utilisé et, comme pour 
l'exemple de bb_threads, deux <foreignphrase>threads</foreignphrase> 
s'exécutent en parallèle.

</para>

<programlisting>
#include &#60;stdio.h&#62;
#include &#60;stdlib.h&#62;
#include "pthread.h"

volatile double pi = 0.0;    /* Approximation de pi (partagée) */
pthread_mutex_t pi_lock;     /* Verrou de la variable ci-dessous */
volatile double intervalles; /* Combien d'intervalles ? */

void *
process(void *arg)
{
  register double largeur, sommelocale;
  register int i;
  register int iproc = (*((char *) arg) - '0');

  /* Fixe la largeur */
  largeur = 1.0 / intervalles;

  /* Fais les calculs locaux */
  sommelocale = 0;
  for (i=iproc; i&#60;intervalles; i+=2) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;

  /* Verrouille la variable pi en vue d'une mise à jour,
     effectue la mise à jour, puis déverrouille Pi. */

  pthread_mutex_lock(&amp;pi_lock);
  pi += sommelocale;
  pthread_mutex_unlock(&amp;pi_lock);

  return(NULL);
}

int
main(int argc, char **argv)
{
  pthread_t thread0, thread1;
  void * retval;

  /* Récupère le nombre d'intervalles */
  intervalles = atoi(argv[1]);

  /* Initialise un verrou sur pi */
  pthread_mutex_init(&amp;pi_lock, NULL);

  /* Crée les deux threads */
  if (pthread_create(&amp;thread0, NULL, process, "0") ||
      pthread_create(&amp;thread1, NULL, process, "1")) {
    fprintf(stderr, "%s: Création des threads impossible\n", argv[0]);
    exit(1);
  }

  /* « Joint » (détruit) les deux threads */
  if (pthread_join(thread0, &amp;retval) ||
      pthread_join(thread1, &amp;retval)) {
    fprintf(stderr, "%s: Erreur à la fusion des threads\n", argv[0]);
    exit(1);
  }

  /* Affiche le résultat */
  printf("Estimation de la valeur de Pi: %f\n", pi);

  /* Sortie */
  exit(0);
}
</programlisting>

</sect2>

<sect2>
<title>La mémoire partagée de System V</title>

<para>

La gestion des IPC (<foreignphrase>Inter-Process 
Communication</foreignphrase>) System V s'effectue au travers d'un 
certain nombre d'appels système fournissant les mécanismes des files de 
message, des sémaphores et de la mémoire partagée. Bien sûr, ces 
mécanismes ont été initialement conçus pour permettre à plusieurs 
processus de communiquer au sein d'un système monoprocesseur. Cela 
signifie néanmoins que ces mécanismes devraient aussi fonctionner dans 
un système Linux SMP, quelque soit le nombre de processeurs.

</para>

<para>

Avant d'aller plus loin dans l'utilisation de ces appels, il est 
important de comprendre que même s'il existe des appels IPC System V 
pour des choses comme les sémaphores et la transmission de messages, 
vous ne les utiliserez probablement pas. Pourquoi&nbsp;? Parce ces 
fonctions sont généralement lentes et sérialisées sous Linux SMP. 
Inutile de s'étendre.

</para>

<para>

La marche à suivre standard pour créer un groupe de processus partageant 
l'accès à un segment de mémoire partagée est la suivante.

</para>

<orderedlist>
<listitem>

<para>

Lancement du programme en tant que processus unique.

</para></listitem>

<listitem><para>

En temps normal, chaque instance de votre programme en parallèle devra 
avoir son propre segment de mémoire partagée, aussi vous faudra-t-il 
appeler <literal>shmget()</literal> pour créer un nouveau segment de la 
taille souhaitée. Mais d'autre part, cet appel peut être utilisé pour 
récupérer l'identifiant d'un segment de mémoire partagée déjà existant. 
Dans les deux cas, la valeur de retour est soit l'identifiant du segment 
de mémoire partagée, soit -1 en cas d'erreur. Par exemple, pour créer un 
segment de mémoire partagée long de <emphasis>b</emphasis> octets, on 
passe un appel ressemblant à

<literal>shmid = shmget(IPC_PRIVATE, b, (IPC_CREAT | 0666))</literal>.

</para></listitem>

<listitem><para>

L'étape suivante consiste à attacher ce segment de mémoire partagée au 
processus, c'est-à-dire l'ajouter à son plan mémoire. Même si l'appel 
<literal>shmat()</literal> permet au programmeur de spécifier l'adresse 
virtuelle à laquelle le segment doit apparaître, cette adresse doit être 
alignée sur une page (plus précisément être un multiple de la taille 
d'une page renvoyée par <literal>getpagesize()</literal>, correspondant 
à 4096 octets), et recouvrera (prendra le pas sur) tout segment de 
mémoire s'y trouvant déjà. Ainsi est-il plus sage de laisser le système 
choisir une adresse. Dans les deux cas, la valeur de retour est un 
pointeur sur l'adresse virtuelle de base du segment fraîchement installé 
dans le plan mémoire. L'instruction correspondante est la 
suivante&nbsp;:

<literal>shmptr = shmat(shmid, 0, 0)</literal>.

Remarquez que vous pouvez allouer toutes vos variables statiques dans ce 
segment de mémoire partagée en déclarant simplement vos variables 
partagées comme étant les membres d'une structure de type 
<literal>struct</literal>, et en déclarant <emphasis>shmptr</emphasis> 
comme étant un pointeur vers ce type de données. Avec cette technique, 
une variable partagée <emphasis>x</emphasis> serait accessible par 
<emphasis>shmptr</emphasis><literal>-&gt;</literal><emphasis>x</emphasis>.

</para></listitem>

<listitem><para>

Comme ce segment de mémoire partagée doit être détruit quand le
dernier processus à y accéder prend fin ou s'en détache, il nous
faut appeler <literal>shmctl()</literal> pour configurer
cette action par défaut. Le code correspondant ressemble à
<literal>shmctl(shmid, IPC_RMID, 0)</literal>.

</para></listitem>

<listitem><para>

Utiliser l'appel Linux <literal>fork()</literal><footnote><para>

N.D.T.&nbsp;: Il s'agit en fait d'un appel Unix standard.

</para></footnote> pour créer le nombre désiré de processus. 
Chacun d'eux héritera du segment de mémoire partagée.

</para></listitem>

<listitem><para>

Lorsqu'un processus a fini d'utiliser un segment de mémoire
partagée, il doit s'en détacher. On accomplit cela par un
<literal>shmdt(shmptr)</literal>.

</para></listitem>

</orderedlist>

<para>

Même avec si peu d'appels système, une fois le segment de mémoire 
partagée établi, tout changement effectué par un processeur sur une 
valeur se trouvant dans cet espace sera automatiquement visible par les 
autres processus. Plus important, chaque opération de communication sera 
exonérée du coût d'un appel système.

</para>

<para>

Ci-après, un exemple de programme en langage C utilisant les segments de 
mémoire partagée System V. Il calcule Pi, en utilisant les algorithmes 
de la section 1.3.

</para>

<programlisting>
#include &#60;stdio.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;unistd.h&#62;
#include &#60;sys/types.h&#62;
#include &#60;sys/stat.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;sys/ipc.h&#62;
#include &#60;sys/shm.h&#62;

volatile struct shared { double pi; int lock; } * partage;

inline extern int xchg(register int reg,
volatile int * volatile obj)
{
  /* Instruction atomique d'échange */
__asm__ __volatile__ ("xchgl %1,%0"
                      :"=r" (reg), "=m" (*obj)
                      :"r" (reg), "m" (*obj));
  return(reg);
}

main(int argc, char **argv)
{
  register double largeur, sommelocale;
  register int intervalles, i;
  register int shmid;
  register int iproc = 0;;

  /* Alloue de la mémoire partagée */
  shmid = shmget(IPC_PRIVATE,
                 sizeof(struct shared),
                 (IPC_CREAT | 0600));
  partage = ((volatile struct shared *) shmat(shmid, 0, 0));
  shmctl(shmid, IPC_RMID, 0);

  /* Fais les inits ... */
  partage-&#62;pi = 0.0;
  partage-&#62;lock = 0;

  /* Crée un fils */
  if (!fork()) ++iproc;

  /* Récupère le nombre d'intervalles */
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;

  /* Fais les calculs locaux */
  sommelocale = 0;
  for (i=iproc; i&#60;intervalles; i+=2) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;

  /* Verrou d'attente atomique, ajout, et déverrouillage ... */
  while (xchg((iproc + 1), &amp;(shared-&#62;lock))) ;
  shared-&#62;pi += sommelocale;
  shared-&#62;lock = 0;

  /* Fin du processus fils (barrière de synchro) */
  if (iproc == 0) {
    wait(NULL);
    printf("Estimation de pi: %f\n", partage-&#62;pi);
  }

  /* Sortie en bonne et due forme */
  return(0);
}
</programlisting>

<para>
Dans cet exemple, j'ai utilisé l'instruction atomique d'échange
pour mettre le verrouillage en &oelig;uvre. Pour de meilleures performances,
préférez-lui une technique de synchronisation évitant les intructions
verrouillant le bus.
</para>

<para>
Pendant les phases de débogage, il est utile de se souvenir que la
commande <literal>ipcs</literal> renvoie la liste des
facilités des IPC System V en cours d'utilisation.
</para>

</sect2>

<sect2>
<title>Projection mémoire (<foreignphrase>Memory Map Call</foreignphrase>)</title>

<para>

L'utilisation des appels système pour accéder aux fichiers (les 
entrées/sorties) peut revenir cher. En fait, c'est la raison pour 
laquelle il existe une bibliothèque de gestion des entrées/sorties 
gérant un tampon dans l'espace utilisateur 
(<literal>getchar()</literal>, <literal>fwrite()</literal>, et cætera). 
Mais les tampons utilisateur ne remplissent pas leur fonction si 
plusieurs processus accèdent au même fichier ouvert en écriture. La 
solution Unix BSD à ce problème fut l'ajout d'un appel système 
permettant à une portion d'un fichier d'être projetée en mémoire 
utilisateur, en utilisant principalement les mécanismes de la mémoire 
virtuelle pour provoquer les mises à jour. Le même mécanisme a été 
utilisé pendant plusieurs années dans les systèmes de Sequent comme base 
de leur gestion du traitement parallèle en mémoire partagée. En dépit de 
commentaires très négatifs dans la page de manuel (assez ancienne), 
Linux semble correctement effectuer au moins quelques unes des fonctions 
de base, et sait prendre en charge l'usage dérivé de cet appel pour 
projeter un segment anonyme de mémoire pouvant être partagé par 
plusieurs processus.

</para>

<para>

L'implémentation Linux de l'appel <literal>mmap()</literal> est en 
elle-même une solution intégrée de remplacement des étapes 2, 3 et 4 du 
schéma classique de mémoire partagée System V, mis en évidence dans la 
section 2.5. Pour créer un segment de mémoire partagée anonyme&nbsp;:

</para>

<programlisting>
shmptr =
    mmap(0,                        /* Le système choisit l'adresse */
         b,                        /* Taille du segment de mémoire partagée */
         (PROT_READ | PROT_WRITE), /* droits d'accès, peuvent être rwx */
         (MAP_ANON | MAP_SHARED),  /* anonyme, partagé */
         0,                        /* descripteur de fichier (inutilisé) */
         0);                       /* offset fichier (inutilisé) */
</programlisting>

<para>

L'équivalent de l'appel de mémoire partagée System V 
<literal>shmdt()</literal> est <literal>munmap()</literal>&nbsp;:

</para>

<programlisting>
munmap(shmptr, b);
</programlisting>

<para>

À mon avis, on ne gagne pas grand chose à utiliser 
<literal>mmap()</literal> plutôt que les mécanismes de gestion de la 
mémoire partagée de System V.

</para>

</sect2>

</sect1>

<sect1>
<title><foreignphrase>Clusters</foreignphrase> de systèmes Linux</title>

<para>

Cette section tente de donner un aperçu de ce qu'est le traitement 
parallèle en <foreignphrase>cluster</foreignphrase> sous Linux. Les 
<foreignphrase>clusters</foreignphrase> sont, actuellement, à la fois 
l'approche la plus populaire et la plus variée, en s'étendant du simple 
réseau de stations de travail (<foreignphrase>Network Of 
Workstations</foreignphrase>&nbsp;: <emphasis>NOW</emphasis>) aux 
machines en parallèle essentiellement construites sur mesure, et dans 
lesquelles il arrive que l'on trouve comme éléments des PC sous Linux. Il 
existe également un grand nombre de logiciels pouvant prendre en charge 
le calcul en parallèle dans des <foreignphrase>clusters</foreignphrase> 
de machines Linux.

</para>

<sect2>
<title>Pourquoi un <foreignphrase>cluster</foreignphrase>&nbsp;?</title>

<para>

Le traitement en parallèle au travers d'un 
<foreignphrase>cluster</foreignphrase> offre plusieurs avantages 
majeurs&nbsp;:

</para>

<itemizedlist>

<listitem><para>

Chacune des machines d'un <foreignphrase>cluster</foreignphrase> peut 
être un système complet, utilisable avec une large gamme d'applications 
de calcul. Cela conduit beaucoup de gens à suggérer l'idée que ce type 
de <foreignphrase>cluster</foreignphrase> pourrait faire usage de tous 
les <quote>cycles machine perdus</quote> sur les ordinateurs tournant à 
ne rien faire dans les bureaux. Il n'est pas si facile de récupérer ces 
cycles, et cela risque de ralentir l'écran de veille de votre collègue, 
mais cela peut être fait.

</para></listitem>

<listitem><para>

L'explosion actuelle des systèmes en réseau signifie que la plupart du 
matériel nécessaire à la construction d'un 
<foreignphrase>cluster</foreignphrase> se vend déjà en grande quantité 
et aux prix réduits inhérents à la <quote>grande distribution</quote>. 
On peut économiser encore un peu plus en partant du principe qu'une 
seule carte vidéo, un seul moniteur et un seul clavier soient 
nécessaires pour chaque <foreignphrase>cluster</foreignphrase> (bien 
qu'il vous faille tout de même les passer d'une machine à une autre pour 
faire l'installation initiale de Linux, une fois lancé, un PC sous Linux 
typique n'a pas besoin de <quote>console</quote>). En comparaison, les 
systèmes SMP et processeurs auxiliaires représentent des marchés plus 
réduits, tendant à proposer des prix plus élevés par performances à 
l'unité.

</para></listitem>

<listitem><para>

Les calculateurs en <foreignphrase>cluster</foreignphrase> peuvent 
<emphasis>évoluer en de très grands systèmes</emphasis>. Alors qu'il est 
actuellement difficile de trouver un ordinateur SMP à plus de quatre 
processeurs qui soit compatible avec Linux, la plupart du matériel 
réseau disponible permet de bâtir facilement un 
<foreignphrase>cluster</foreignphrase> incluant jusqu'à 16 machines. 
Avec un peu d'huile de coude, on peut mettre en réseau des centaines 
voire des milliers de machines. En fait, Internet tout entier pourrait 
être assimilé à un seul immense <foreignphrase>cluster</foreignphrase>.

</para></listitem>

<listitem><para>

Le fait que remplacer une <quote>mauvaise machine</quote> au sein d'un 
<foreignphrase>cluster</foreignphrase> soit une opération triviale 
comparé à la réparation d'un ordinateur SMP partiellement défaillant 
garantit une disponibilité bien plus élevée aux configurations de 
<foreignphrase>cluster</foreignphrase> soignées. Cela devient important 
non seulement pour les applications qui ne peuvent tolérer des 
interruptions de service trop importantes, mais aussi dans l'utilisation 
en général de systèmes qui contiennent un nombre suffisant de 
processeurs pour que la panne d'une machine en particulier soit assez 
courante (par exemple, même si la durée moyenne avant la première panne 
d'un PC est environ deux ans, dans un 
<foreignphrase>cluster</foreignphrase> de 32 machines, la probabilité 
qu'au moins une machine tombe en panne dans les six premiers mois est 
assez élevée).

</para></listitem>

</itemizedlist>

<para>

Très bien. Si les <foreignphrase>clusters</foreignphrase> sont gratuits 
ou très peu onéreux, peuvent devenir très grands et offrir une haute 
disponibilité&hellip; pourquoi tout le monde n'utilise pas un 
<foreignphrase>cluster</foreignphrase>&nbsp;? Eh bien, parce qu'il y a 
aussi des problèmes&nbsp;:

</para>

<itemizedlist>

<listitem><para>

À quelques exceptions près, le matériel réseau n'est pas conçu pour le 
traitement en parallèle. Typiquement, les temps de latence sont élevés 
et la bande passante réduite comparés aux systèmes SMP et processeurs 
auxiliaires. Par exemple, si les temps de latence d'un SMP n'excèdent 
généralement pas plus de quelques microsecondes, ils atteignent 
couramment des centaines, voire des milliers de microsecondes dans un 
<foreignphrase>cluster</foreignphrase>. La bande passante des 
communications dans un système SMP dépasse souvent 100 mégaoctets par 
seconde. Bien que le matériel réseau le plus rapide (c'est-à-dire le 
<quote>Gigabit Ethernet</quote>) présente une vitesse comparable, la 
plupart des réseaux sont de 10 à 1000 fois plus lents.

La performance d'un matériel réseau est déjà suffisamment médiocre dans 
un <foreignphrase>cluster</foreignphrase> <emphasis>isolé</emphasis>. Si 
le réseau n'est pas isolé du reste du trafic, ce qui est souvent le cas 
lorsque l'on utilise des <quote>machines qui sont en réseau</quote> 
plutôt qu'un système conçu pour être un 
<foreignphrase>cluster</foreignphrase>, les performances peuvent être 
bien pires encore.

</para></listitem>

<listitem><para>

La gestion logicielle des <foreignphrase>clusters</foreignphrase> en 
tant que système unique est très mince. Par exemple, la commande 
<literal>ps</literal> ne fait état que des processus s'exécutant sur un 
seul système Linux, pas des processus s'exécutant à travers le 
<foreignphrase>cluster</foreignphrase> Linux entier.

</para></listitem>

</itemizedlist>

<para>

Moralité, les <foreignphrase>clusters</foreignphrase> ont un très grand 
potentiel, mais ce potentiel risque d'être très difficile à concrétiser 
pour la plupart des applications. La bonne nouvelle, c'est qu'il existe 
un soutien logiciel très développé pour vous aider à obtenir de bonnes 
performances avec les programmes adaptés à cet environnement, et qu'il 
existe également des réseaux conçus spécialement pour élargir la palette 
de programmes pouvant avoir de bonnes performances.

</para>

</sect2>

<sect2>
<title>Le matériel réseau</title>

<para>

Les réseaux informatiques sont en pleine explosion&hellip; mais vous le 
savez déjà. Un nombre toujours grandissant de technologies et produits 
réseau a été développé, et la plupart d'entre eux sont disponibles sous 
une forme qui peut être utilisée pour faire d'un groupe de machines (des 
PC fonctionnant chacun sous Linux) un 
<foreignphrase>cluster</foreignphrase> de traitement en parallèle.

</para>

<para>

Malheureusement, aucune technologie réseau ne résout complètement tous 
les problèmes. À vrai dire, le nombre d'approches différentes, en coût 
et en performances, est à première vue assez difficile à croire. Par 
exemple, le coût par machine mise en réseau s'étend de moins de 5 
dollars jusqu'à plus de 4000. La bande passante et les temps de latence 
varient aussi selon quatre ordres de grandeur.

</para>

<para>

Avant d'en apprendre plus sur les spécificités de certains réseaux, il 
est important de remarquer que ces choses évoluent très fréquemment 
(voir <ulink url="http://www.linux.org.uk/NetNews.html"/> pour avoir des 
nouvelles fraîches concernant les réseaux sous Linux), et qu'il est très 
difficile d'obtenir des infos précises concernant certains réseaux.

</para>

<para>

J'ai placé un <quote><emphasis>?</emphasis></quote> aux endroits 
incertains. J'ai aussi passé beaucoup de temps à faire des recherches 
sur ce sujet, mais je reste sûr que ce résumé est plein d'erreurs et 
d'omissions importantes. Si vous avez des corrections ou des ajouts à y 
apporter, merci de m'envoyer un courrier électronique en anglais à 
l'adresse suivante&nbsp;: <email>&addr-auteur;</email>.

</para>

<para>

Les résumés comme le <ulink 
url="http://www.mcgeoch.com/other/lan-technology.html">LAN Technology 
Scorecard</ulink><footnote><para>

N.D.T.&nbsp;: il semble que ce panel ne soit plus mis à jour depuis un 
certain temps. Le site suggéré proposait à la date de rédaction de la 
version française le bulletin le plus récent, mais n'est pas officiel.

</para></footnote> donnent les caractéristiques de nombreux et 
différents types de réseaux et de standards de réseaux locaux (LAN). 
Cependant, l'essentiel de ce guide pratique est centré sur les propriétés les 
plus indiquées à la construction d'un 
<foreignphrase>cluster</foreignphrase> Linux. Chaque section décrivant 
un type de réseau débute par une courte liste de caractéristiques. Ci 
dessous, la définition de chaque entrée.

</para>

<variablelist>

<varlistentry>
<term>Prise en charge sous Linux&nbsp;:</term>

<listitem><para>

Si la réponse est <emphasis>non</emphasis>, la signification est claire. 
Les autres réponses tentent de décrire l'interface de base utilisée pour 
accéder au réseau. La plupart du matériel réseau est interfacé via un 
pilote de périphérique du noyau, sachant typiquement gérer les 
communications TCP/UDP. D'autres réseaux utilisent des interfaces plus 
directes (par exemple des bibliothèques) pour réduire les temps de 
latence en évitant d'avoir à passer par le noyau.

</para>

<para>

Il y a quelques années, il était considéré comme parfaitement acceptable 
d'accéder au coprocesseur mathématique (<quote><foreignphrase>floating 
point unit</foreignphrase></quote>) par un appel système mais 
aujourd'hui, c'est clairement ridicule. À mon avis, nécessiter un appel 
système pour chaque communication entre les processeurs exécutant un 
programme en parallèle est peu commode. Le problème est que les 
ordinateurs n'ont pas encore intégré ces mécanismes de communication, et 
que les approches non <quote>orientées noyau</quote> tendent à présenter 
des problèmes de portabilité. Vous allez entendre beaucoup parler de ce 
sujet dans un avenir proche, principalement sous la forme de la nouvelle 
<emphasis> Virtual Interface (VI) Architecture</emphasis> (<ulink 
url="http://www.intel.com/intelpress/sum_via.htm"/>) méthode 
standardisée pour les opérations des interfaces réseau et servant à 
contourner les couches des systèmes d'exploitation usuels. Le standard 
VI est appuyé par Compaq, Intel et Microsoft, et aura assurément un 
impact important sur la conception des SAN (<foreignphrase>System Area 
Network</foreignphrase>) dans les prochaines années.

</para></listitem>

</varlistentry>

<varlistentry>
<term>Bande passante maximum&nbsp;:</term>
<listitem><para>

C'est le chiffre dont tout le monde se soucie. J'ai généralement repris 
les débits maximum théoriques. Votre moyenne, elle, 
<emphasis>va</emphasis> varier.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Temps de latence minimum&nbsp;:</term>
<listitem><para>

À mon avis, c'est le chiffre dont tout le monde devrait se soucier, plus 
encore que de la bande passante. Là encore, j'ai utilisé les 
(irréalistes) valeurs théoriques idéales, mais au moins ces nombres 
prennent en compte <emphasis>toutes</emphasis> les sources de latence, 
tant logicielles que matérielles. Dans la plupart des cas, les temps de 
latence réseau ne durent que quelques microsecondes. Des nombres 
beaucoup plus grands reflètent les couches des matériels et logiciels 
inefficaces.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Disponibilité&nbsp;:</term>
<listitem><para>

Reprise telle quelle, cette ligne décrit la forme sous laquelle vous 
pouvez acquérir ce type de matériel. Le matériel de la grande 
distribution est disponible largement et de plusieurs fabricants, le 
prix étant alors le premier critère de choix. Les choses proposées par 
<quote>différents fabricants</quote> sont disponibles chez plus d'un 
seul concurrent, mais il existe des différences significatives, et des 
problèmes potentiels d'interopérabilité. Les réseaux produits par un 
<quote>fabricant exclusif</quote> vous laissent à la merci de ce 
fournisseur, aussi bienveillant soit-il. <quote>Domaine public</quote> 
signifie que même si vous ne pouvez trouver quelqu'un pour vous vendre 
ce type de matériel, vous ou n'importe qui d'autre pouvez acheter les 
composants et en fabriquer un exemplaire. C'est typiquement le cas des 
prototypes de recherche. Ils ne sont en général ni prêts à être utilisés 
par le public, ni disponibles à celui-ci.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Port ou bus utilisé&nbsp;:</term>

<listitem><para>

La manière dont on relie l'interface réseau à l'ordinateur. Les 
meilleures performances (et désormais les plus courantes) s'obtiennent 
avec le bus PCI. Il existe aussi des cartes pour bus EISA, VESA local 
bus (VLB), et ISA. ISA fut le premier, et il est toujours utilisé par 
les cartes aux basses performances. On trouve toujours l'EISA comme bus 
secondaire dans les machines PCI. Ces derniers temps, on ne trouve plus 
beaucoup de matériel à base de VLB (même si la <ulink 
url="http://www.vesa.org">Video Electronics Standards 
Association</ulink> voit les choses autrement).

</para>

<para>

Bien sûr, toute interface que vous pouvez utiliser sans avoir à ouvrir 
le boîtier de votre PC est plus qu'attrayante. les interfaces IrDA 
(N.D.T.&nbsp;: port infrarouge) et USB font leur apparition de plus en 
plus fréquemment. Le Port Parallèle Standard (SPP) est longtemps resté 
<quote>ce sur quoi vous branchiez votre imprimante</quote>, mais s'est 
avéré dernièrement être très utile comme extension du bus ISA. Cette 
nouvelle fonction est améliorée par le standard IEEE 1284, qui spécifie 
les optimisations EPP et ECP. Il y a aussi le bon vieux port série 
RS232, lent mais fiable. Je n'ai eu vent d'aucun témoignage concernant 
l'interconnexion de machines par le biais des connecteurs vidéo (VGA), 
clavier, souris ou joystick&hellip;

</para></listitem>

</varlistentry>

<varlistentry>
<term>Structure du réseau&nbsp;:</term>
<listitem><para>

Un bus est un fil, un ensemble de fils, ou une fibre (optique). Un 
<foreignphrase>hub</foreignphrase> (<quote>concentrateur</quote> ou 
<quote>plaque tournante</quote>) est une petite boite qui peut recevoir 
différents types de fils ou de fibres. Les commutateurs 
(<quote><foreignphrase>switched hubs</foreignphrase></quote>) permettent 
à plusieurs connexions de transmettre activement et simultanément leurs 
données.

</para></listitem>
</varlistentry>

<varlistentry>
<term>Coût par machine reliée&nbsp;:</term>
<listitem><para>

Voici comment lire ces nombres. Supposons que, en dehors des connexions 
réseau, acheter un PC comme unité de votre 
<foreignphrase>cluster</foreignphrase> vous coûte 2000 dollars. L'ajout 
de Fast Ethernet porte le prix à l'unité à environ 2400 dollars. L'ajout 
de Myrinet mène ce prix à 3800 dollars environ. Si vous avez 20&nbsp;000 
dollars à dépenser, vous pouvez alors avoir soit 8 machines reliées par 
du Fast Ethernet, soit 5 machines reliées par du Myrinet. Il est 
également très raisonnable d'utiliser plusieurs types de réseaux. Par 
exemple, avec 20&nbsp;000 dollars, vous pouvez vous offrir 8 machines 
reliées entre elles par Fast Ethernet et TTL_PAPERS. Choisissez un 
réseau &mdash; ou un ensemble de réseaux &mdash; qui permettra à votre 
<foreignphrase>cluster</foreignphrase> d'exécuter votre application le 
plus rapidement possible.

</para>

<para>

Au moment où vous lirez ces lignes, ces chiffres seront faux&hellip; En 
fait, ils le sont sûrement déjà. Il peut aussi y avoir un grand nombre 
de réductions, d'offres spéciales, et cætera. Mais en tout cas, les prix 
cités ici ne seront jamais suffisamment erronés pour vous conduire à 
faire un choix totalement inapproprié. Nul besoin d'avoir un doctorat 
(même si c'est mon cas ;-) pour constater que l'utilisation de réseaux 
onéreux n'a de sens que si votre application a réellement besoin de 
leurs propriétés ou si les PC utilisés dans votre 
<foreignphrase>cluster</foreignphrase> sont eux aussi relativement 
chers.

</para></listitem>

</varlistentry>

</variablelist>

<para>
Maintenant que vous êtes avertis, place au spectacle&hellip;
</para>

<sect3>
<title>ArcNet</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>2,5 Mbits/s</emphasis>
</para>
</listitem>
<listitem>

<para>
Temps de latence minimum&nbsp;: <emphasis>1000 microsecondes&nbsp;?</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>
</para>
</listitem>
<listitem>

<para>
Port ou bus utilisé&nbsp;: <emphasis>ISA</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis><foreignphrase>Hub</foreignphrase> ou bus non commutés (anneau logique)</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine reliée&nbsp;: <emphasis>200 dollars</emphasis>
</para>
</listitem>

</itemizedlist>

</para>

<para>
ARCNET est un réseau local principalement destiné à être utilisé
dans les systèmes de contrôle temps réel embarqués. Comme Ethernet,
le réseau est physiquement organisé en prises le long d'un bus d'un
ou plusieurs <foreignphrase>hubs</foreignphrase>. En revanche, et contrairement
à Ethernet, il utilise un protocole à base de jetons qui structure de manière
logique le réseau comme un anneau. Les entêtes des paquets sont réduites (3 ou 4
octets) et les messages peuvent être très courts (jusqu'à un seul octet).
De fait, ARCNET est plus efficace qu'Ethernet. Malheureusement, il est
aussi plus lent, et moins populaire ce qui le rend plus cher. Vous trouvez
plus d'informations sur le site de l'<ulink
url="http://www.arcnet.com/">ARCNET Trade Association</ulink>.
</para>

</sect3>

<sect3>
<title>ATM</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau, bibliothèques AAL*</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>155 Mbits/s</emphasis> (bientôt, <emphasis>1200 Mbits/s</emphasis>)
</para>
</listitem>
<listitem>

<para>
Temps de latence minimum&nbsp;: <emphasis>120 microsecondes</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>
</para>
</listitem>
<listitem>

<para>
Port ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis><foreignphrase>hubs</foreignphrase> commutés</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine reliée&nbsp;: <emphasis>3000 dollars</emphasis>
</para>
</listitem>

</itemizedlist>

</para>

<para>
A moins d'avoir été dans le coma ces dernières années, vous avez
sûrement beaucoup entendu dire qu'ATM (<quote><foreignphrase>Asynchronous Transfer Mode</foreignphrase></quote>)
<emphasis>est</emphasis> l'avenir&hellip; Eh bien, en quelque sorte.
ATM est meilleur marché que HiPPI et plus rapide que Fast Ethernet,
et il peut être utilisé sur de très longues distances, ce qui
intéresse beaucoup les opérateurs téléphoniques. Le protocole du
réseau ATM est également conçu pour fournir une interface logicielle
aux temps d'accès réduits et gérant plus efficacement les messages
courts et les communications en temps réel (c'est-à-dire les transmissions
audio et vidéo numériques). C'est aussi l'un des réseaux aux plus hauts
débits que Linux prenne actuellement en charge. La mauvaise nouvelle,
c'est qu'ATM n'est pas vraiment bon marché, et qu'il demeure encore des
problèmes de compatibilité entre fabricants. Un aperçu du développement
ATM sous Linux est disponible sur <ulink
url="http://linux-atm.sourceforge.net/"
>http://linux-atm.sourceforge.net/</ulink
>.
</para>

</sect3>

<sect3>
<title>CAPERS</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>bibliothèque AFAPI</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>1,2 Mbit/s</emphasis>
</para>
</listitem>
<listitem>

<para>
Temps de latence maximum&nbsp;: <emphasis>3 microsecondes</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>grande distribution</emphasis>
</para>
</listitem>
<listitem>

<para>
Port ou bus utilisé&nbsp;: <emphasis>SPP (port parallèle)</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis>câble entre 2 machines</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine reliée&nbsp;: <emphasis>2 dollars</emphasis>
</para>
</listitem>

</itemizedlist>

</para>

<para>

CAPERS (<quote><foreignphrase>Cable Adapter for Parallel Execution and 
Rapid Synchronisation</foreignphrase></quote>&nbsp;: <quote>Adaptateur 
par Câble pour l'Exécution en Parallèle et la Synchronisation 
Rapide</quote>) est un produit dérivé du projet PAPERS, de 
l'<foreignphrase>University School of Electrical and Computer 
Engineering</foreignphrase> de Purdue. En substance, ce projet définit 
un protocole logiciel permettant d'utiliser une simple liaison 
point-à-point par le port parallèle, type <quote>LapLink</quote>, pour 
implémenter la bibliothèque PAPERS sur deux PC sous Linux. L'idée n'est 
exploitable à grande échelle, mais le prix est imbattable. Tout comme avec 
TTL_PAPERS, pour améliorer la sécurité du système, il est recommandé, 
mais pas nécessaire, d'appliquer un correctif mineur au noyau.

</para>

</sect3>

<sect3>
<title>Ethernet</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>10 Mbits/s</emphasis>
</para>
</listitem>
<listitem>

<para>
Latence minimum&nbsp;: <emphasis>100 microsecondes</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>grande distribution</emphasis>
</para>
</listitem>
<listitem>

<para>
Port ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis>commutateurs ou concentrateurs, ou même bus simple</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine connectée&nbsp;: <emphasis>100 dollars</emphasis> (sans concentrateur, <emphasis>50 dollars</emphasis>)
</para>
</listitem>

</itemizedlist>

</para>

<para>

Depuis plusieurs années maintenant, l'Ethernet à 10Mbits/s est le 
standard des technologies réseau. Une bonne carte Ethernet s'achète pour 
moins de 50 dollars, et bon nombre de PC en sont aujourd'hui équipés de 
série, l'interface étant intégrée à la carte-mère. Les réseaux à base 
Ethernet dont la charge n'est pas très élevée peuvent être organisés 
comme un long bus à prises multiples sans concentrateur 
(<quote><foreignphrase>hub</foreignphrase></quote>). Une telle 
configuration peut servir jusqu'à 200 machines pour un coût minimal, 
mais n'est pas appropriée au traitement en parallèle. Ajouter un 
concentrateur non commuté n'améliore pas beaucoup les performances. En 
revanche, les commutateurs 
(<quote><foreignphrase>switches</foreignphrase></quote>), qui peuvent 
offrir une bande passante maximum à plusieurs connexions simultanément 
ne coûtent qu'aux alentours de 100 dollars par port. Linux prend en 
charge un nombre impressionnant d'interfaces Ethernet différentes, mais 
il est important de garder à l'esprit que les variations entre ces 
matériels peuvent engendrer de grandes différences de performance. 
Consultez le <ulink url="&howto;Hardware-HOWTO.html">Guide pratique de 
la compatibilité matérielle avec Linux</ulink> (N.D.T.&nbsp;: version 
française du <ulink url="http://www.tldp.org/HOWTO/Hardware-HOWTO/"> 
<foreignphrase>Hardware&nbsp;Compatibility&nbsp;HOWTO</foreignphrase> 
</ulink>) pour plus d'informations concernant les différents équipements 
fonctionnant sous Linux, et pour avoir une idée de leur qualité.

</para>

<para>

Le <foreignphrase>cluster</foreignphrase> Linux à 16 machines réalisé 
dans le cadre du projet <quote><ulink 
url="http://www.beowulf.org">Beowulf</ulink></quote> (initialement 
développé au CESDIS de la NASA) est une manière intéressante d'augmenter 
ses performances. C'est à Donald Becker, auteur de plusieurs pilotes de 
cartes Ethernet, que l'on doit la prise en charge de la répartition du 
trafic au travers de plusieurs cartes réseau s'éclipsant mutuellement 
(autrement dit, partageant les mêmes adresses réseau). Cette fonction 
est intégrée en standard dans Linux, et s'effectue de manière invisible 
en dessous du niveau des opérations sur les 
<foreignphrase>sockets</foreignphrase>. Le coût d'un hub n'étant pas 
négligeable, relier chaque machine à deux (ou plus) réseaux Ethernet, 
sans <foreignphrase>hubs</foreignphrase> ni 
<foreignphrase>switches</foreignphrase>, pour améliorer les performances 
peut s'avérer financièrement très rentable. D'une manière générale, dans 
les situations où une machine est le goulet d'étranglement d'un réseau, 
la répartition de charge à travers plusieurs réseaux 
(<quote><foreignphrase>shadow networks</foreignphrase></quote>) se 
révèle bien plus efficace que l'usage d'un réseau équipé d'un 
commutateur seul.

</para>

</sect3>

<sect3>
<title>Ethernet (Fast Ethernet)</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>
</para>
</listitem>
<listitem><para>

Bande passante maximum&nbsp;: <emphasis>100 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>80 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>grande distribution</emphasis>

</para></listitem>

<listitem><para>

Port ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>concentrateurs ou commutateurs 
(<foreignphrase lang="en">hubs</foreignphrase> ou <foreignphrase 
lang="en">switches</foreignphrase>)</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>400 dollars (?)</emphasis>

</para></listitem>

</itemizedlist>

</para>

<para>

Bien qu'il existe un certain nombre de technologies différentes nommées 
<quote>Fast Ethernet</quote>, elles se réfèrent souvent à un réseau 
Ethernet 100 Mbits/s en grande partie compatible avec les anciens câbles 
et périphériques 10 Mbits/s type <quote>10 BaseT</quote>. Comme on peut 
s'y attendre, tout ce qui s'appelle Ethernet bénéficie des prix de la 
vente de masse, et ces interfaces ne coûtent généralement qu'une 
fraction du prix des cartes ATM à 155 Mbits/s. Le problème est qu'une 
collection de machines partageant tous le même <quote>bus</quote> à 100 
Mbits/s (à l'aide d'un <foreignphrase>hub</foreignphrase> non commuté) 
peut présenter des performances n'atteignant en moyenne même pas celles 
d'un réseau 10 Mbits/s utilisant un commutateur fournissant à chaque 
machine une connexion 10 Mbits/s complète.

</para>

<para>
Les commutateurs pouvant fournir une connexion 100 Mbits à chaque
machine simultanément sont chers, mais les prix chutent chaque jour,
et ces commutateurs offrent une bande passante autrement plus élevée
que de simples <foreignphrase>hubs</foreignphrase> non commutés. Ce qui rend les commutateurs ATM
si onéreux est la nécessité de commuter chaque cellule ATM, cellule
relativement courte. Certains commutateurs Ethernet parient sur une
fréquence de commutation attendue relativement lente et en tirent
profit en utilisant des techniques aux temps de latence réduits à
l'intérieur du commutateur, mais nécessitant quelques millisecondes
pour changer de voie. Si l'itinéraire de votre trafic réseau change
fréquemment, évitez ce type d'équipement.
</para>

<para>
Notez aussi que, comme pour Ethernet, le projet Beowulf
(<ulink
url="http://www.beowulf.org"
>http://www.beowulf.org</ulink
>) de la NASA développe des pilotes aux performances supérieures
car utilisant la répartition de charge entre plusieurs cartes
Fast Ethernet.
</para>

</sect3>

<sect3>
<title>Ethernet (Gigabit Ethernet)</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>1000 Mb/s</emphasis>
</para>
</listitem>
<listitem>

<para>
Temps de latence minimum&nbsp;: <emphasis>300 microsecondes (?)</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>
</para>
</listitem>
<listitem>

<para>
Port ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure réseau&nbsp;: <emphasis>commutateurs ou FDR</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine connectée&nbsp;: <emphasis>2500 dollars (?)</emphasis>
</para>
</listitem>

</itemizedlist>

</para>

<para>
Je ne suis pas sûr que <ulink
url="http://www.gigabit-ethernet.org/">Gigabit Ethernet</ulink>
ait une raison technologique valable
de s'appeler Ethernet&hellip; mais son nom inspire le fait que
Gigabit Ethernet est conçu pour être une technologie réseau
bon marché et de grande distribution, avec une prise en charge
native de l'IP. En revanche, les prix actuels reflètent le
fait que cela reste un produit difficile à fabriquer.
</para>

<para>

Contrairement aux autres technologies Ethernet, Gigabit Ethernet apporte 
un contrôle du flux, ce qui devrait en faire un réseau plus fiable. Les 
FDR, ou <quote><foreignphrase>Full-Duplex 
Repeaters</foreignphrase></quote> (<quote>répéteurs bidirectionnels 
simultanés</quote>), se contentent de multiplexer les lignes, en 
utilisant des mémoires tampons et des contrôles de flux localisés pour 
améliorer les performances. La plupart des commutateurs sont construits 
comme de nouveaux modules pour les différents modèles de commutateurs 
compatible Gigabit déjà existants. Les commutateurs ou FDR sont 
distribués ou annoncés par au moins Acacianet, <ulink 
url="http://www.baynetworks.com/">Bay networks</ulink>, <ulink 
url="http://www.cabletron.com/">Cabletron</ulink> (désormais Enterasys. 
Page française&nbsp;: <ulink url="http://www.enterasys.com/fr/"/>), 
Networks digital, <ulink 
url="http://www.extremenetworks.com/homepage_french.asp">Extreme 
networks</ulink>, <ulink url="http://www.foundrynet.com/">Foundry 
networks</ulink>, Gigalabs.com, Packet engines, <ulink 
url="http://www.plaintree.com/">Plaintree systems</ulink>, <ulink 
url="http://www.prominet.com/">Prominet</ulink>, <ulink 
url="http://fr.sun.com/">Sun microsystems</ulink>, et Xlnt.

</para>

<para>

Il existe un pilote pour Linux pour les <quote>Yellowfin</quote> G-NIC 
de Packet Engines<footnote><para>

N.D.T.&nbsp;: désormais intégré aux sources du noyau.

</para></footnote>. Les premiers essais sous Linux ont fait état d'un 
taux de transfert environ 2,5 fois supérieur à la plus rapide des cartes 
Fast Ethernet à 100 Mbits/s. Avec les réseaux Gigabit, une configuration 
soigneuse du bus PCI est un facteur critique. Il reste toujours un doute 
quant à la poursuite des améliorations de ce pilote, et du développement 
des pilotes Linux des autres cartes réseau.

</para>

</sect3>

<sect3>
<title>FC (<quote><foreignphrase>Fibre Channel</foreignphrase></quote>)</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: 
<emphasis>non</emphasis><footnote><para>

N.D.T.&nbsp;: les pilotes FC pour le noyau Linux ont en fait été écrits 
en 1999.

</para></footnote>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>1062 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>PCI (?)</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>?</emphasis>

</para></listitem>

</itemizedlist>

<para>

L'objectif du FC (<quote><foreignphrase>Fibre 
Channel</foreignphrase></quote>) est de fournir un médium 
d'entrée/sortie de type bloc aux performances élevées (une trame FC 
transporte un bloc de données d'une longueur forfaitaire de 2048 
octets), particulièrement adapté aux partages de disques et autres 
périphériques de stockage, qui peuvent alors être reliés directement au 
réseau FC plutôt qu'à travers un ordinateur. Niveau bande passante, le 
FC est présenté comme étant relativement rapide, avec un taux s'étendant 
de 133 à 1062 Mbits/s. Si le FC tend à devenir populaire en tant que 
solution haut-de-gamme de remplacement du SCSI, il pourrait rapidement 
devenir une technologie très abordable. Pour le moment, ce n'est pas 
abordable, et ce n'est pas pris en charge par Linux. La Fibre Channel 
Association tient une importante collection de références au FC, sur 
<ulink url="http://www.fibrechannel.org"/><footnote><para>

N.D.T.&nbsp;: La <foreignphrase>Fibre Channel 
Association</foreignphrase> (FCA) et la <foreignphrase>Fibre Channel 
Loop Community</foreignphrase> (FCLC) ont fusionné en 2000 pour former 
la <foreignphrase>Fibre Channel Industry Association</foreignphrase>.

</para></footnote>.

</para>

</sect3>

<sect3>
<title>FireWire (IEEE 1394)</title>

<para>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>non</emphasis><footnote>
<para>

N.D.T.&nbsp;: les premiers pilotes FireWire pour Linux ont été écrits en 
1999 mais, bien que disponibles en standard, sont toujours considérés 
comme expérimentaux.

</para></footnote>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>196,608 Mbits/s</emphasis> (bientôt, <emphasis>393,216 Mbits/s</emphasis>)
</para>
</listitem>
<listitem>

<para>
Temps de latence minimum&nbsp;: <emphasis>?</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>
</para>
</listitem>
<listitem>

<para>
Interface ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis>aléatoire, sans cycles (auto-configuré)</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine connectée&nbsp;: <emphasis>600 dollars</emphasis>
</para>
</listitem>

</itemizedlist>

</para>

<para>
FireWire, ou standard IEEE 1394-1995, est voué à être le réseau numérique
à grande vitesse et à prix réduit des appareils électroniques domestiques.
Son application-phare est la connexion des caméscopes numériques aux
ordinateurs, mais FireWire est destiné à être utilisé dans des domaines
s'étendant de l'alternative au SCSI jusqu'à l'interconnexion des différents
composants de votre <quote>Home Cinéma</quote>. Il vous permet de relier plus de
64000 périphériques dans une topologie utilisant des bus et des ponts
mais ne formant pas de boucle, et détecte automatiquement la configuration
des périphériques lorsqu'ils sont ajoutés ou retirés. Les messages courts
(les <quote>quadlets</quote>, longs de quatre octets) sont également pris en charge,
de même que les transmissions isochrones sur le modèle de l'ATM (utilisées
pour préserver le synchronisme des messages multimédia). Adaptec propose
des produits FireWire permettant de relier jusqu'à 63 périphériques
à une seule carte PCI, et tient aussi un bon site d'information générale
concernant le FireWire sur
<ulink url="http://www.adaptec.com/worldwide/product/prodtechindex.html?cat=%2fTechnology%2fFireWire-1394&amp;prodkey=1394_summary">http://www.adaptec.com</ulink>.
</para>

<para>
Bien que le FireWire ne soit pas le réseau le plus rapide disponible
actuellement, le marché de la grande distribution (qui tire les prix
vers le bas) et les temps de latence réduits pourraient en faire d'ici
l'an prochain la meilleure interface réseau pour le <emphasis><foreignphrase>message passing</foreignphrase></emphasis> dans
un <foreignphrase>cluster</foreignphrase> de PC sous Linux.
</para>

</sect3>

<sect3>
<title>HiPPI et Serial HiPPI</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: 
<emphasis>non</emphasis><footnote><para>

N.D.T.&nbsp;: HiPPI est aujourd'hui pris en charge par Linux, mais de 
façon restreinte et expérimentale.

</para></footnote>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>1600 Mbits/s</emphasis> 
(<emphasis>1200 Mb/s</emphasis> pour Serial HiPPI)

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>EISA, PCI</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>commutateurs</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>3500 dollars</emphasis> 
(<emphasis>4,500</emphasis> dollars pour Serial HiPPI)

</para></listitem>

</itemizedlist>

<para>

HiPPI (<quote><foreignphrase>High Performance Parallel 
Interface</foreignphrase></quote>, soit <quote>Interface Parallèle aux 
Performances Élevées</quote>) était initialement censée fournir un taux 
de transfert élevé pour l'échange d'immenses blocs de données entre un 
supercalculateur et une autre machine (un autre supercalculateur, un 
<quote><foreignphrase>frame buffer</foreignphrase></quote>, une batterie 
de disques, et cætera), et est devenu le standard dominant dans le monde 
des supercalculateurs. Bien que ce soit un oxymoron, <emphasis>Serial 
HiPPI</emphasis> devient également très populaire en utilisant 
typiquement de la fibre optique à la place des câbles HiPPI standard de 
32 bits de large (donc parallèles). Ces dernières années, les 
commutateurs HiPPI en croix sont devenus courants et les prix ont 
sérieusement chuté. Malheureusement, les équipement HiPPI Série, eux, 
sont encore très onéreux, et sont en général les seuls pris en charge 
par le bus PCI. Pire, Linux ne gère pas encore HiPPI. Le CERN tient une 
bonne présentation d'HiPPI sur <ulink 
url="http://www.cern.ch/HSI/hippi/"/>. Ils tiennent aussi une liste 
assez longue de distributeurs proposant le HiPPI sur <ulink 
url="http://www.cern.ch/HSI/hippi/procintf/manufact.htm"/>.

</para>

</sect3>

<sect3>
<title>IrDA (<quote><foreignphrase>Infrared Data Association</foreignphrase></quote>)</title>

<itemizedlist>
<listitem>

<para>

Prise en charge par Linux&nbsp;: <emphasis>non 
(?)</emphasis><footnote><para>

N.D.T.&nbsp;: l'IrDA est pris en charge par le noyau depuis 2000.

</para></footnote>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>1,15 Mbits/s</emphasis> et 
<emphasis>4 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>Différents fabricants</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>IrDA</emphasis>

</para></listitem>

<listitem><para>

Structure réseau&nbsp;: <emphasis>Air libre</emphasis> ;-)

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>0</emphasis>

</para></listitem>

</itemizedlist>

<para>

L'IrDA (<quote><foreignphrase>Infrared Data 
Association</foreignphrase></quote> ou <quote>Association de Données par 
Infrarouges</quote>, sur <ulink url="http://www.irda.org"/>), c'est ce 
petit appareil à infrarouges sur le coté des ordinateurs portables. Il 
reste assez difficile, par conception, de relier plus de deux 
ordinateurs par ce biais, aussi l'IrDA ne se prête-t-il guère à la 
<quote>clusterisation</quote>, ou mise en parallèle massive de 
nombreuses machines. Don Becker est toutefois l'auteur de quelques 
travaux préliminaires sur l'IrDA.

</para>

</sect3>

<sect3>
<title>Myrinet</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>bibliothèques</emphasis>

</para></listitem>
<listitem><para>

Bande passante maximum&nbsp;: <emphasis>1280 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>9 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>matériel propriétaire.</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisés&nbsp;: <emphasis>PCI</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>commutateurs</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>1800 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

<ulink url="http://www.myri.com">Myrinet</ulink> est un réseau local 
(LAN&nbsp;: <quote><foreignphrase>Local Area 
Network</foreignphrase></quote>) conçu pour servir également de réseau 
système <footnote><para>

SAN&nbsp;: <quote><foreignphrase>System Area 
Network</foreignphrase></quote>.

</para>
<para>

N.D.T.&nbsp;: à ne pas confondre avec <quote><foreignphrase>Storage Area 
Network</foreignphrase></quote>, qui partage le même acronyme.

</para></footnote>. Les versions LAN et SAN utilisent des médias 
physiques distincts et leur caractéristiques sont sensiblement 
différentes. La version SAN est généralement utilisée au sein d'un 
<foreignphrase>cluster</foreignphrase>.

</para>

<para>

La structure de Myrinet est très conventionnelle, mais a la réputation
d'être très bien implémentée. Les pilotes pour Linux sont connus pour
donner de très bons résultats, bien qu'il eût été fait état de frappantes
différences de performances d'une implémentation du bus PCI à l'autre.

</para>

<para>

Actuellement, Myrinet est assurément le réseau favori des responsables 
de <foreignphrase>clusters</foreignphrase> n'étant pas trop sévèrement 
limité au niveau budgétaire. Si, pour vous, un PC Linux typique est un 
Pentium Pro dernier cri ou un Pentium II avec au moins 256&nbsp;Mo de mémoire 
vive, et un disque RAID et SCSI, alors le coût de Myrinet apparaît 
raisonnable. En revanche, avec des machines plus conventionnelles, il 
vous faudra probablement choisir entre relier <emphasis>N</emphasis> 
machines avec Myrinet, ou <emphasis>2N</emphasis> machines avec 
plusieurs équipements de type <quote>Fast Ethernet</quote> ou 
<quote>TTL_PAPERS</quote>. Tout cela dépend réellement de votre budget 
et du type de calcul qui vous importe le plus.

</para>

</sect3>

<sect3>
<title>Parastation</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>couches d'abstraction 
(<quote><foreignphrase>HAL</foreignphrase></quote>) ou bibliothèques 
réseau</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>125 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>2 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>fabricant exclusif</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>maillage, sans concentrateur</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>plus de 1000 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

Le projet ParaStation (<ulink 
url="http://wwwipd.ira.uka.de/parastation"/>) de la section informatique 
de l'Université de Karlsruhe est en train de mettre sur pieds un réseau 
<quote>maison</quote> compatible PVM et aux temps de latence réduits. 
Ils ont d'abord construit un prototype de ParaPC biprocesseur en 
utilisant une carte EISA conçue sur mesure et des PC fonctionnant sous 
Unix BSD, puis ont bâti de plus grands 
<foreignphrase>clusters</foreignphrase> composés de machines Alpha DEC. 
Depuis Janvier 1997, Parastation est disponible sous Linux. Les cartes 
PCI sont produites en coopération avec une société nommée <ulink 
url="http://www.hitex.com">Hitex</ulink>. Le matériel de Parastation 
implémente à la fois un système de transmission de messages rapide et 
fiable, et des barrières de synchronisation simples.

</para>

</sect3>

<sect3>
<title>PLIP</title>

<itemizedlist>
<listitem>

<para>
Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>
</para>
</listitem>
<listitem>

<para>
Bande passante maximum&nbsp;: <emphasis>1,2 Mbits/s</emphasis>
</para>
</listitem>
<listitem>

<para>
Temps de latence minimum&nbsp;: <emphasis>1000 microsecondes&nbsp;?</emphasis>
</para>
</listitem>
<listitem>

<para>
Disponibilité&nbsp;: <emphasis>grande distribution</emphasis>
</para>
</listitem>
<listitem>

<para>
Interface ou bus utilisé&nbsp;: <emphasis>SPP</emphasis>
</para>
</listitem>
<listitem>

<para>
Structure du réseau&nbsp;: <emphasis>câble entre 2 machines</emphasis>
</para>
</listitem>
<listitem>

<para>
Coût par machine connectée&nbsp;: <emphasis>2 dollars</emphasis>
</para>
</listitem>

</itemizedlist>

<para>

Pour le seul coût d'un câble <quote>LapLink</quote> (N.D.T.&nbsp;: câble 
parallèle croisé), PLIP (<quote><foreignphrase>Parallel Line Interface 
Protocol</foreignphrase></quote>, soit <quote>Protocole d'Interface par 
Ligne Parallèle</quote>) permet à deux machines Linux de communiquer par 
le port parallèle en utilisant les couches logicielles standard basées 
sur la communication par <quote>sockets</quote>. En termes de bande 
passante, de temps de latence et d'évolutivité, il ne s'agit pas d'une 
technologie réseau sérieuse. En revanche, le coût de revient quasi-nul 
et la compatibilité logicielle s'avèrent être très utiles. Le pilote est 
partie intégrante du noyau Linux standard.

</para>

</sect3>

<sect3>

<title>SCI</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>non</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>4000 Mbit/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>2,7 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>différents fabricants.</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>PCI et propriétaire</emphasis>

</para></listitem>

<listitem><para>

Structure réseau&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>+ de 1000 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

L'objectif de SCI (Scalable Coherent Interconnect, ANSI/IEEE 1596-1992) 
consiste essentiellement à fournir un mécanisme de haute performance 
pouvant assurer des accès cohérents à la mémoire partagée au travers 
d'un grand nombre de machines. On peut dire sans se mouiller que la 
bande passante et les temps de latences de SCI sont <quote>très 
impressionnants</quote> comparés à la plupart des autres technologies 
réseau. Le problème est que SCI n'est pas très répandu et reste donc 
assez onéreux, et que ce matériel n'est pas pris en charge par Linux.

</para>

<para>

SCI est principalement utilisé dans diverses implémentations 
propriétaires pour des machines à mémoire partagée logiquement et 
distribuée physiquement, comme le HP/Convex Exemplar SPP et le Sequent 
NUMA-Q 2000 (voir <ulink url="http://www.sequent.com"/><footnote><para>

N.D.T.&nbsp;: Sequent a été absorbé par IBM en septembre 1999.

</para></footnote>). Ceci dit, SCI est disponible sous forme de 
carte PCI et de commutateurs quatre ports de Dolphin (on peut relier 
ainsi jusqu'à 16 machines en montant ces commutateurs en cascade), 
<ulink url="http://www.dolphinics.com"/>, sous la série "Clustar". Le 
CERN tient à jour une bonne collection de liens concernant SCI sur 
<ulink url="http://www.cern.ch/HSI/sci/sci.html"/>.

</para>

</sect3>

<sect3>
<title>SCSI</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: de <emphasis>5 Mbits/s</emphasis> à plus 
de <emphasis>20 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>différents fabricants</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>cartes PCI, EISA ou 
ISA</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>bus inter-machines partageant des 
périphériques SCSI</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>?</emphasis>

</para></listitem>

</itemizedlist>

<para>

Le SCSI (Small Computer Systems Interconnect) consiste essentiellement 
en un bus d'entrée/sortie utilisé par les disques durs, les lecteurs de 
CD-ROM, les numériseurs 
(<quote><foreignphrase>scanners</foreignphrase></quote>), et cætera. Il 
existe trois standards distincts&nbsp;: SCSI-1, SCSI-2 et SCSI-3, en 
vitesses "Fast" et "Ultra" et en largeur de bus de 8, 16 ou 32 bits 
(avec compatibilité FireWire annoncée pour SCSI-3). Tout cela est plutôt 
confus, mais nous savons tous qu'un bon SCSI est bien plus rapide que 
l'EIDE, et peut gérer plus de périphériques, plus efficacement.

</para>

<para>

Ce que beaucoup de gens ne réalisent pas, c'est qu'il est très simple de 
partager le même bus SCSI entre deux ordinateurs. Ce type de 
configuration est très utile pour partager des disques entre deux 
machines et mettre en place un système de 
<emphasis><foreignphrase>fail-over</foreignphrase></emphasis>, de façon 
à ce qu'une machine prenne à sa charge les requêtes à une base de 
données lorsque l'autre machine tombe en panne. C'est actuellement le 
seul mécanisme reconnu par le <foreignphrase>cluster</foreignphrase> PC 
de Microsoft&nbsp;: WolfPack. En revanche, l'incapacité du SCSI à 
évoluer vers de plus grands systèmes le rend en général inintéressant 
pour le traitement en parallèle.

</para>

</sect3>

<sect3>
<title>ServerNet</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>non</emphasis>

</para></listitem>

<listitem><para>

Maximum bandwidth&nbsp;: <emphasis>400 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>3 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>fabricant exclusif</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>PCI</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>arbre hexagonal / concentrateurs en 
mailles tétraédriques</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>?</emphasis>

</para></listitem>

</itemizedlist>

<para>

ServerNet est la solution réseau de haute performance proposée par 
Tandem (<ulink url="http://www.tandem.com"/>). Dans le monde du 
traitement des transactions en ligne (<quote><foreignphrase>OnLine 
Transation Processing</foreignphrase></quote>, ou <quote>OLTP</quote>) 
en particulier, Tandem est réputé être l'un des premiers fabricants de 
systèmes de haute fiabilité, aussi n'est-il pas surprenant que leurs 
réseaux ne revendiquent pas simplement la haute performance, mais aussi 
la <quote>haute fiabilité et intégrité des données</quote>. Une autre 
facette intéressante de ServerNet&nbsp;: ce matériel serait capable de 
transférer des données directement de périphérique à périphérique, pas 
simplement entre processeurs, mais également entre disques durs, et 
cætera, dans un style unilatéral similaire à ce qui a été suggéré pour 
les mécanismes d'accès à distance à la mémoire du MPI, décrits dans la 
section 3.5. Un dernier mot à propos de Servernet&nbsp;: Bien qu'il n'y 
ait qu'un seul fabricant, celui-ci est suffisamment puissant pour faire 
établir potentiellement Servernet en tant que standard majeur&nbsp;: 
Tandem appartient à Compaq<footnote><para>

N.D.T.&nbsp;: et donc désormais à Hewlett-Packard.

</para></footnote>.

</para>

</sect3>

<sect3>
<title>SHRIMP</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>interface utilisateur à 
mémoire mappée</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>180 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>5 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>prototype expérimental</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>EISA</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>Fond de panier en maille (comme 
pour le Paragon d'Intel)</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>?</emphasis>

</para></listitem>

</itemizedlist>

<para>

Le projet <ulink 
url="http://www.cs.princeton.edu/shrimp/">SHRIMP</ulink> de la section 
des sciences des ordinateurs de l'Université de Princeton, met sur pieds 
un ordinateur parallèle en utilisant dont les éléments de traitement 
sont des ordinateurs PC sous Linux. Le premier SHRIMP 
(<quote><foreignphrase>Scalable, High-Performance, Really Inexpensive 
Multi-Processor</foreignphrase></quote>, soit <quote>multiprocesseur 
évolutif et de hautes performances vraiment bon marché</quote>) était un 
simple prototype biprocesseur utilisant une mémoire partagée sur une 
carte EISA développée pour l'occasion. Il existe désormais un prototype 
pouvant évoluer vers de plus larges configurations en utilisant une 
interface <quote>maison</quote> pour se connecter à une sorte de 
concentrateur, essentiellement conçu comme le réseau de routage en 
mailles utilisé dans le Paragon d'Intel. Des efforts considérables ont 
été faits pour développer une électronique de <quote>communication 
mappée en mémoire virtuelle</quote> aux 
<foreignphrase>overheads</foreignphrase> réduits, avec sa couche 
logicielle.

</para>

</sect3>

<sect3>
<title>SLIP</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>0,1 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>1000 microsecondes&nbsp;?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>grande distribution</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>RS232C</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>câble entre deux machines</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>2 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

Même si SLIP (<quote><foreignphrase>Serial Line Interface 
Protocol</foreignphrase></quote>) se situe définitivement au pied de 
l'échelle des performances, ce protocole (tout comme CSLIP ou PPP) 
permet à deux machines de communiquer en utilisant les 
<quote><foreignphrase>sockets</foreignphrase></quote> et ce au travers 
d'un câble RS232 ordinaire. Les ports RS232 peuvent être reliés à l'aide 
d'un câble série type NULL-MODEM, ou même au travers d'une ligne 
téléphonique en utilisant des modems. Dans tous les cas, les temps de 
latence sont élevés, et la bande passante réduite. Aussi, SLIP ne 
devrait être utilisé qu'en dernier recours. En revanche, la plupart des 
PC sont dotés de deux ports RS232. Il doit donc être possible de relier 
un groupe de machines sous forme de réseau linéaire ou d'anneau. Il 
existe même un logiciel de répartition de la charge appelé EQL.

</para>

</sect3>

<sect3>
<title>TTL_PAPERS</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>bibliothèque AFAPI</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>1,6 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>3 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>conception dans le domaine public, 
fabricant exclusif</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>SPP (port parallèle)</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>arbre de concentrateurs</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>100 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

Le projet PAPERS (<quote><foreignphrase>Purdue's Adapter for Parallel 
Execution and Rapid Synchronization</foreignphrase></quote>, soit 
<quote>Adaptateur pour l'Exécution en Parallèle et la Synchronisation 
Rapide de l'université de Purdue</quote>), mené par la Purdue University 
School of Electrical and Computer Engineering (<quote>École Supérieure 
d'Électricité et d'Ingénierie en Informatique</quote>), développe un 
ensemble logiciel et matériel évolutif et aux temps de latence réduits 
pour les communications des fonctions d'agrégation, permettant de mettre 
sur pieds un supercalculateur en parallèle utilisant comme n&oelig;uds 
des PC d'origine, non modifiés.

</para>

<para>

Plus d'une douzaine de versions de cartes <quote>PAPERS</quote>, reliées 
au PC à la station de travail via le port parallèle standard (SPP&nbsp;: 
<quote><foreignphrase>Standard Parallel Port</foreignphrase></quote>), 
ont été construites, en suivant globalement deux grands axes. Les 
versions estampillées <quote>PAPERS</quote> visent les hautes 
performances, quelle que soit la technologie la plus indiquée, la 
version actuelle utilisant des FPGA (N.D.T.&nbsp;: famille de réseaux 
logiques programmables), et des modèles d'interfaces PCI à haut débit 
sont actuellement à l'étude. Par opposition, les versions nommées 
<quote>TTL_PAPERS</quote> sont conçues pour être facilement reproduites 
hors de l'université de Purdue, et s'appuient sur des modèles du domaine 
public remarquablement simples et qui peuvent être mis en place en 
utilisant de la logique TTL ordinaire. L'une de ces versions est 
produite commercialement.

</para>

<para>

Contrairement au matériel sur mesure conçu par d'autres universités, des 
<foreignphrase>clusters</foreignphrase> TTL_PAPERS ont été assemblés 
dans plusieurs écoles depuis les États-Unis jusqu'en Corée du Sud. La 
bande passante est sévèrement limitée par la connectivité du port 
parallèle, mais PAPERS met en &oelig;uvre des fonctions d'agrégation aux 
temps de latence très réduits. Même les systèmes orientés messages les 
plus rapides ne peuvent offrir de telles performances sur ces fonctions 
d'agrégation. Ainsi, PAPERS est particulièrement performant dans la 
synchronisation des différents écrans d'un mur vidéo (à débattre dans le 
<foreignphrase>Video-Wall-HOWTO</foreignphrase> actuellement en 
préparation), pour planifier les accès à un réseau à haut débit, pour 
évaluer les probabilités en recherche génétique, et cætera. Même si des 
<foreignphrase>clusters</foreignphrase> PAPERS ont été construits en 
utilisant AIX d'IBM sur PowerPC, des DEC Alpha OSF/1, ou HP-UX sur HP 
PA-RISC, le PC sous Linux reste la plate-forme la mieux prise en charge.

</para>

<para>

Les programmes utilisateur utilisant l'AFAPI de TTL_PAPERS attaquent 
directement les registres matériels du port parallèle sous Linux, sans 
effectuer d'appel système à chaque accès. Pour ce faire, l'AFAPI demande 
d'abord les droits d'accès au port parallèle en utilisant soit 
<literal>iopl()</literal>, soit <literal>ioperm()</literal>. Le problème 
est que, l'un comme l'autre, ces appels obligent le programme appelant à 
être privilégié, ce qui introduit une faille de sécurité potentielle. La 
solution réside en un correctif optionnel à appliquer au noyau Linux et 
permettant à un processus privilégié de contrôler les permissions 
d'accès aux ports d'entrée/sortie pour n'importe quel autre processus.

</para>

</sect3>

<sect3>
<title>USB (<quote><foreignphrase>Universal Serial Bus</foreignphrase></quote>)</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>pilotes du noyau</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>12 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence minimum&nbsp;: <emphasis>?</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>dans le commerce</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>USB</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>bus</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>5 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

USB (<quote><foreignphrase>Universal Serial Bus</foreignphrase></quote>, 
<ulink url="http://www.usb.org"/>) est un bus fonctionnant à la vitesse 
de l'Ethernet conventionnel, dont les périphériques qui s'y rattachent 
peuvent être connectés à chaud 
(<quote><foreignphrase>Hot-Plug</foreignphrase></quote>&nbsp;: sans 
imposer la mise hors tension préalable du bus) et pouvant accueillir simultanément 
jusqu'à 127 de ces périphériques pouvant s'étendre du clavier à la 
caméra de vidéo-conférence. La manière dont on relie plusieurs 
ordinateurs par le biais de l'USB n'est pas clairement définie. Quoi 
qu'il en soit, les ports USB sont en train de s'établir très rapidement 
en standard sur les cartes-mères, au même titre que le port série RS232 
ou le port parallèle, aussi ne soyez pas surpris si vous voyez 
apparaître un ou deux ports USB <footnote><para>

N.D.T.&nbsp;: les ports et le standard USB sont aujourd'hui parfaitement 
reconnus par Linux.

</para></footnote> sur votre prochain PC.

</para>

<para>
D'une certaine manière, l'USB est pratiquement la version basse performance
à prix nul du FireWire que l'on peut se procurer aujourd'hui.
</para>

</sect3>

<sect3>
<title>WAPERS</title>

<itemizedlist>

<listitem><para>

Prise en charge par Linux&nbsp;: <emphasis>bibliothèque AFAPI</emphasis>

</para></listitem>

<listitem><para>

Bande passante maximum&nbsp;: <emphasis>0,4 Mbits/s</emphasis>

</para></listitem>

<listitem><para>

Temps de latence&nbsp;: <emphasis>3 microsecondes</emphasis>

</para></listitem>

<listitem><para>

Disponibilité&nbsp;: <emphasis>modèle dans le domaine public</emphasis>

</para></listitem>

<listitem><para>

Interface ou bus utilisé&nbsp;: <emphasis>SPP (Port Parallèle Standard)</emphasis>

</para></listitem>

<listitem><para>

Structure du réseau&nbsp;: <emphasis>modèle de câblage entre 2 à 64 machines</emphasis>

</para></listitem>

<listitem><para>

Coût par machine connectée&nbsp;: <emphasis>5 dollars</emphasis>

</para></listitem>

</itemizedlist>

<para>

WAPERS (<quote><foreignphrase>Wired-AND Adapter for Parallel Execution 
and Rapid Synchronization</foreignphrase></quote>, soit 
<quote>Adaptateur par ET Câblé pour l'Exécution en Parallèle et la 
Synchronisation Rapide</quote>) est une des facettes du projet PAPERS, 
de la Purdue University School of Electrical and Computer Engineering 
(<quote>École Supérieure d'Électricité et d'Ingénierie en 
Informatique</quote>). S'il est construit proprement, le port parallèle 
possède quatre bits de sortie à collecteur ouvert qui peuvent être 
câblés entre eux pour former un ET câblé de 4 bits de large. Ce ET câblé 
est assez sensible électriquement, et le nombre maximum de machines qui 
peuvent y être reliées dépend de façon critique des propriétés 
analogiques des ports (les limites maximum des puits de courant et les 
valeurs des résistances de <foreignphrase>pull-up</foreignphrase>). On 
peut typiquement relier 7 à 8 machines en réseau de cette façon, avec 
WAPERS. Bien que les coûts et les temps de latences soient très réduits, 
la bande passante l'est aussi. WAPERS est donc bien plus indiqué comme 
réseau secondaire dédié aux fonctions d'agrégations que comme unique 
réseau d'un <foreignphrase>cluster</foreignphrase>. Comme pour 
TTL_PAPERS, il existe un correctif noyau visant à améliorer la sécurité, 
recommandé mais non requis.

</para>

</sect3>

</sect2>

<sect2>
<title>Interface Logicielle Réseau</title>

<para>

Avant d'explorer les ressources logicielles existantes en matière de 
traitement en parallèle, il est utile de couvrir rapidement les bases de 
l'interface logicielle de bas niveau gérant l'électronique du réseau. Il 
n'existe en réalité que trois options de base&nbsp;: les 
<foreignphrase>sockets</foreignphrase>, les pilotes de périphériques et 
les bibliothèques utilisateur.

</para>

<sect3>
<title>Les <foreignphrase>sockets</foreignphrase></title>

<para>

Le <foreignphrase>socket</foreignphrase> est de loin la plus courante 
des interfaces réseau de bas niveau. Les 
<foreignphrase>sockets</foreignphrase> sont partie intégrante d'Unix 
depuis plus d'une décennie et la plupart des standards dans le domaine 
du matériel électronique de réseau est conçue pour prendre en charge au 
moins deux types de protocoles de 
<foreignphrase>socket</foreignphrase>&nbsp;: TCP et UDP. Ces deux types 
de <foreignphrase>sockets</foreignphrase> vous permettent d'envoyer des 
blocs de données d'une longueur arbitraire d'une machine à l'autre, mais 
il existe plusieurs différences importantes. Typiquement, ils engendrent 
tous deux un temps de latence minimum d'environ 1000 microsecondes, même 
si les performances peuvent bien pires encore en fonction du trafic.

</para>

<para>

Ces types de <foreignphrase>sockets</foreignphrase> constituent 
l'interface logicielle réseau de base pour la majorité des logiciels de 
traitement en parallèle portables et de plus haut niveau. Par exemple, 
PVM utilise une combinaison de l'UDP et du TCP, aussi en connaître les 
différences vous aidera à affiner les performances de votre système. Ce 
qui suit n'est qu'un aperçu de TCP et UDP. Référez-vous aux pages du 
manuel et à un bon livre de programmation pour plus de détails.

</para>

<sect4>
<title>Le protocole UDP (SOCK_DGRAM)</title>

<para>

<emphasis>UDP</emphasis> signifie <quote><foreignphrase>User Datagram 
Protocol</foreignphrase></quote> ou <quote>Protocole de Datagrammes 
Utilisateur</quote> mais il est plus facile de se souvenir des 
propriétés d'UDP en tant que <quote><foreignphrase>Unreliable Datagram 
Processing</foreignphrase></quote>, ou <quote>Traitement des Datagrammes 
Peu fiable</quote>. En d'autres termes, UDP permet à chaque bloc d'être 
émis comme un message individuel, mais un message peut être perdu 
pendant la transmission. De fait, selon l'état du trafic sur le réseau, 
certains messages UDP peuvent être perdus, arriver plusieurs fois, ou 
arriver dans un ordre différent de celui dans lequel ils ont été émis. 
L'expéditeur d'un message UDP ne reçoit pas systématiquement d'accusé de 
réception, et c'est donc au programme écrit par l'utilisateur qu'il 
appartient de détecter et compenser ces problèmes. Heureusement, le 
protocole UDP garantit que si un message arrive, son contenu sera intact 
(c'est-à-dire que vous ne recevrez jamais un message incomplet).

</para>

<para>

Le bon coté de l'UDP est qu'il tend à être le plus rapide des protocoles 
des <foreignphrase>socket</foreignphrase>. En outre, UDP est 
<quote>orienté hors connexion</quote> 
(<quote><foreignphrase>connectionless</foreignphrase></quote>), ce qui 
signifie que chaque message est essentiellement indépendant des autres. 
On peut comparer chaque message à une lettre à La Poste. Vous pouvez 
envoyer plusieurs lettres à la même adresse, mais chacune d'entre elles 
est indépendante des autres, et vous n'êtes pas limité quant aux nombre 
de personnes à qui vous pouvez en envoyer.

</para>

</sect4>

<sect4>
<title>Le protocole TCP (SOCK_STREAM)</title>

<para>

Contrairement à l'UDP, le <emphasis>TCP</emphasis> est un protocole 
fiable et orienté connexion. Chaque bloc est considéré non pas comme un 
message, mais comme un bloc de données appartenant à un flot d'octets 
voyageant au travers d'une connexion établie entre l'expéditeur et le 
destinataire. Ce principe est très différent du système de messages de 
l'UDP car chaque bloc n'est qu'une partie du flot d'octets, et il 
appartient au programme utilisateur de trouver le moyen de les isoler 
car il n'y a aucune marque de séparation pour les distinguer. De plus, 
les connexions sont plus vulnérables aux perturbations du réseau, et 
seul un nombre limité de connexions simultanées peut exister au sein 
d'un même processus. Parce qu'il est fiable, le TCP engendre souvent des 
<foreignphrase>overheads</foreignphrase> plus importants que l'UDP.

</para>

<para>

Le TCP réserve en revanche quelques bonnes surprises. Par exemple, si 
plusieurs messages sont envoyés à travers une connexion, TCP est capable 
de les rassembler dans une mémoire tampon pour mieux correspondre aux 
tailles standard des paquets de l'électronique du réseau, ce qui peut 
donner de meilleurs résultats que l'UDP dans le cas de groupes de 
messages courts ou de taille inhabituelle. Un autre avantage&nbsp;: Les 
réseaux bâtis sur des connexions physiques directes entre deux machines 
peuvent facilement et efficacement être assimilés à des connexions TCP. 
Ce fut le cas pour la <quote><foreignphrase>Socket 
Library</foreignphrase></quote> (<quote>bibliothèque de gestion de 
<foreignphrase>Sockets</foreignphrase></quote>), présentant une gestion 
compatible TCP au niveau de l'utilisateur qui ne différait des appels 
systèmes TCP standard que par le préfixe <literal>PSS</literal>, que 
l'on rajoutait au début du nom des fonctions à invoquer.

</para>

</sect4>

</sect3>

<sect3>
<title>Les pilotes de périphériques</title>

<para>

Lorsque l'on en arrive au stade où il faut effectivement injecter des 
données sur le réseau ou les y en extraire, l'interface logicielle des 
Unix standard fait partie du noyau et s'appelle <quote>pilote</quote> 
(<quote><foreignphrase>driver</foreignphrase></quote>). UDP et TCP ne se 
contentent pas de transporter des données, ils s'accompagnent également 
d'importants <foreignphrase>overheads</foreignphrase> dus à la gestion 
des <foreignphrase>sockets</foreignphrase>. Par exemple, il faut que 
quelque chose s'occupe du fait que plusieurs connexions TCP peuvent 
partager la même interface réseau physique. Par opposition, un pilote de 
périphérique dédié à une interface réseau n'a besoin de mettre en 
&oelig;uvre qu'un petit nombre de fonctions de transport élémentaires. 
Ces pilotes peuvent alors être invoqués à l'aide de l'appel 
<literal>open()</literal> pour identifier le périphérique adéquat, puis 
en utilisant par exemple <literal>read()</literal> et 
<literal>write()</literal> sur le <quote>fichier</quote> ouvert. Ainsi, 
chaque opération peut transporter un bloc de données en coûtant à peine 
plus cher qu'un appel système, ce qui permet d'atteindre des délais de 
l'ordre de quelques dizaines de microsecondes.

</para>

<para>

Écrire un pilote de périphérique pour Linux n'est pas difficile&hellip; 
pourvu que vous sachiez <emphasis>parfaitement</emphasis> comme 
fonctionne votre périphérique. Si vous n'en êtes pas sûr, n'essayez pas 
de le deviner. Déboguer un pilote de périphérique n'est pas une chose 
amusante, et faire des erreurs peut coûter la vie à votre matériel. En 
revanche, si ces risques ne vous effraient pas, il est possible d'écrire 
un pilote pour, par exemple, utiliser des cartes Ethernet dédiées comme 
des connexions machine-vers-machine <quote>bêtes</quote> mais très 
rapides car exonérées du protocole Ethernet habituel. Pour être exact, 
c'est pratiquement la technique utilisée par les premiers 
supercalculateurs Intel. Référez-vous au 
<foreignphrase>Device-Driver-HOWTO</foreignphrase> pour plus 
d'informations.

</para>

</sect3>

<sect3>
<title>Bibliothèques utilisateurs</title>

<para>

Si vous avez pris des cours de programmation système, on a dù vous y 
apprendre qu'accéder directement aux registres matériels des 
périphériques à partir d'un programme utilisateur était l'exemple 
typique de ce qu'il ne faut pas faire, parce que l'un des principes même 
d'un système d'exploitation est de contrôler l'accès aux périphériques. 
Cependant, le simple fait de passer un appel système coûte au minimum 
quelques dizaines de microsecondes. Dans le cas d'interfaces réseau 
bâties sur mesure comme TTL_PAPERS, qui peut effectuer des opérations de 
base sur un réseau en seulement 3 microsecondes, un tel surcoût pour un 
appel système est intolérable. Le seul moyen d'éviter ce temps d'attente 
est de faire en sorte que du code s'exécutant au niveau de 
l'utilisateur, donc une <quote>bibliothèque au niveau de 
l'utilisateur</quote> <footnote><para>

N.D.T.&nbsp;: par opposition à un <quote>appel système</quote>, donc sans franchir
la barrière du passage en mode noyau.

</para></footnote>, puisse accéder directement au matériel, mais sans 
remettre en cause la souveraineté du système d'exploitation sur la 
gestion des droits d'accès aux ressources matérielles.

</para>

<para>
Sur un système typique, les seuls moyens, pour une bibliothèque utilisateur,
d'accéder directement aux registres du matériel sont les suivants&nbsp;:
</para>

<orderedlist>

<listitem><para>

Au lancement du programme utilisateur, faire un appel système pour 
mapper l'espace d'adressage qui contient les registres du périphérique 
dans le plan mémoire du processus utilisateur. Sur certains systèmes, 
l'appel système <literal>mmap()</literal> (traité pour la première fois 
dans la section 2.6) peut être utilisé pour mapper un fichier spécial 
représentant les adresses de la page de mémoire physique du 
périphérique. Il est en même temps relativement simple d'écrire un 
pilote de périphérique effectuant cette opération. De plus, ce pilote 
peut obtenir l'accès en ne mappant que la ou les pages qui contiennent 
les registres nécessaires, en maintenant ainsi le contrôle des droits 
d'accès sous la coupe du système d'exploitation.

</para></listitem>

<listitem><para>

Accéder ensuite aux registres du périphérique sans passer par un appel 
système en se contentant de charger ou de ranger des valeurs sur la 
plage d'adressage mappée. Par exemple, un

<literal>*((char *) 0x1234) = 5;</literal>

déposera un octet de valeur 5 à l'adresse mémoire 1234 en hexadécimal.

</para></listitem>

</orderedlist>

<para>
Par bonheur, il se trouve que Linux pour Intel 386 et compatibles
offre une solution meilleure encore&nbsp;:
</para>

<orderedlist>

<listitem><para>

En invoquant l'appel système <literal>ioperm()</literal>
depuis un processus privilégié, obtenir la permission d'accéder
aux ports d'entrée/sortie correspondant précisément aux registres
du périphérique. Parallèlement, ces permissions peuvent être gérées
par un processus utilisateur privilégié et indépendant (autrement
dit&nbsp;: un <quote>méta-système d'exploitation</quote>) en utilisant l'appel
 système <emphasis>giveioperm()</emphasis>, disponible sous la
forme d'un correctif à appliquer au noyau
Linux.

</para></listitem>

<listitem><para>

Accéder aux registres du périphérique sans appel système en
utilisant les instructions assembleur d'accès aux ports
d'entrée/sortie du 386.

</para></listitem>

</orderedlist>

<para>
Cette seconde solution est préférable car il arrive souvent que
les registres de plusieurs périphériques soient réunis sur une
même page, auquel cas la première méthode ne pourrait offrir
de protection contre l'accès aux autres registres résidant dans
la même page que ceux appartenant au périphérique concerné.
L'inconvénient est que ces instructions ne peuvent bien sûr pas
être écrites en langage C. Il vous faudra à la place utiliser
un peu d'assembleur. La fonction utilisant l'assembleur en ligne
intégré à GCC (donc utilisable dans les programmes C) permettant
de lire un octet depuis un port est la suivante&nbsp;:
</para>

<programlisting>
extern inline unsigned char
inb(unsigned short port)
{
    unsigned char _v;
__asm__ __volatile__ ("inb %w1,%b0"
                      :"=a" (_v)
                      :"d" (port), "0" (0));
    return _v;
}
</programlisting>

<para>
La fonction symétrique permettant l'émission d'un octet est&nbsp;:
</para>

<para>

<programlisting>
extern inline void
outb(unsigned char value,
unsigned short port)
{
__asm__ __volatile__ ("outb %b0,%w1"
                      :/* pas de valeur retournée */
                      :"a" (value), "d" (port));
}
</programlisting>

</para>

</sect3>

</sect2>

<sect2>
<title>PVM (<quote><foreignphrase>Parallel Virtual Machine</foreignphrase></quote>)</title>

<para>

PVM (pour <quote><foreignphrase>Parallel Virtual 
Machine</foreignphrase></quote>, soit <quote>Machine Virtuelle en 
Parallèle</quote>) est une bibliothèque de 
<foreignphrase>message-passing</foreignphrase> portable et disponible 
gratuitement, s'appuyant généralement directement sur les 
<foreignphrase>sockets</foreignphrase>. Cette bibliothèque s'est 
incontestablement établie comme le standard <foreignphrase>de 
facto</foreignphrase> dans le domaine du traitement en parallèle à 
l'aide de <foreignphrase>clusters</foreignphrase> à transmission de 
messages.

</para>

<para>

PVM prend en charge les machines Linux monoprocesseur et SMP, comme les 
<foreignphrase>clusters</foreignphrase> de machines Linux reliées entre 
elles à l'aide par des réseaux reconnaissant les 
<foreignphrase>sockets</foreignphrase> (donc SLIP, PLIP, Ethernet, ATM). 
En fait, PVM fonctionnera même à travers un groupe de machines utilisant 
différents types de processeurs, de configurations et de réseaux 
physiques &mdash; Un <emphasis>cluster hétérogène</emphasis> &mdash; 
même si l'envergure de ce <foreignphrase>cluster</foreignphrase> est de 
l'ordre de la mise en parallèle de machines en utilisant Internet pour 
les relier entre elles. PVM offre même des facilités de contrôles de 
tâches (<quote><foreignphrase>jobs</foreignphrase></quote>) en parallèle 
au travers d'un <foreignphrase>cluster</foreignphrase>. Cerise sur le 
gâteau, PVM est disponible gratuitement et depuis longtemps 
(actuellement sur <ulink 
url="http://www.epm.ornl.gov/pvm/pvm_home.html"/>), ce qui a conduit bon 
nombre de langages de programmation, de compilateurs, et d'outils de 
débogage ou autres à l'adopter comme leur <quote>bibliothèque cible 
portable de <foreignphrase>message-passing</foreignphrase></quote>. Il 
existe également un groupe de discussion&nbsp;: <ulink 
url="news:comp.parallel.pvm"/>.

</para>

<para>

Il est important de remarquer, en revanche, que les appels PVM ajoutent 
généralement aux opérations <foreignphrase>socket</foreignphrase> un 
<foreignphrase>overhead</foreignphrase> non négligeable alors que les 
temps de latence de celles-ci sont déjà importants. En outre, les appels 
eux-mêmes ne sont pas aisés à manipuler.

</para>

<para>
Appliquée au même exemple de calcul de Pi décrit en section 1.3,
la version PVM du programme en langage C est la suivante&nbsp;:
</para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;
#include &#60;pvm3.h&#62;

#define NPROC   4

main(int argc, char **argv)
{
  register double sommelocale, largeur;
  double somme;
  register int intervalles, i;
  int mytid, iproc, msgtag = 4;
  int tids[NPROC];  /* Tableau des numéros des tâches */

  /* Début du traitement avec PVM */
  mytid = pvm_mytid();

  /* « Je rejoins le groupe et, si je suis la première instance,
     iproc=0, je crée plusieurs copies de moi-même. »
  */
  iproc = pvm_joingroup("pi");

  if (iproc == 0) {
    tids[0] = pvm_mytid();
    pvm_spawn("pvm_pi", &amp;argv[1], 0, NULL, NPROC-1, &amp;tids[1]);
  }
  /* On s'assure que tous les processus sont prêts  */
  pvm_barrier("pi", NPROC);

  /* Récupère le nombre d'intervalles */
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;

  sommelocale = 0.0;
  for (i = iproc; i&#60;intervalles; i+=NPROC) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }

  /* On ajuste les résultats locaux en fonction de la largeur */
  somme = sommlocale * largeur;
  pvm_reduce(PvmSum, &amp;sum, 1, PVM_DOUBLE, msgtag, "pi", 0);

  /* Seul le processus rattaché à la console renvoie le résultat */
  if (iproc == 0) {
    printf("Estimation de la valeur de pi: %f\n", somme);
  }

  /* On attend que le programme soit terminé,
     on quitte le groupe et
         on sort de PVM. */
  pvm_barrier("pi", NPROC);
  pvm_lvgroup("pi");
  pvm_exit();
  return(0);
}
</programlisting>

</sect2>

<sect2>
<title>MPI (<quote><foreignphrase>Message Passing Interface</foreignphrase></quote>)</title>

<para>

Bien que PVM soit le standard de fait en matière de bibliothèque de 
<foreignphrase>message-passing</foreignphrase>, MPI 
(<quote><foreignphrase>Message Passing 
Interface</foreignphrase></quote>) tend à devenir le nouveau standard 
officiel. Le site du standard MPI se trouve sur <ulink 
url="http://www.mcs.anl.gov:80/mpi/"/> et le groupe de discussion 
correspondant sur <ulink url="news:comp.parallel.mpi"/>.

</para>

<para>
En revanche, avant d'explorer MPI, je me sens obligé de parler
rapidement de la guerre de religion qui oppose PVM à MPI et qui
dure depuis quelques années. Je ne penche ni pour l'un ni pour
l'autre. Voici un résumé aussi impartial que possible des
différences entre les deux interfaces&nbsp;:
</para>

<variablelist>

<varlistentry>
<term>Environnement de contrôle de l'exécution</term>
<listitem>
<para>
Pour faire simple, PVM en a un, et MPI ne précise pas
s'il existe, ni comment il doit être implémenté. Cela
signifie que certaines choses comme lancer l'exécution
d'un programme PVM se fait de la même manière partout,
alors que pour MPI, cela dépend de l'implémentation
utilisée.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Prise en charge des <foreignphrase>clusters</foreignphrase> hétérogènes.</term>
<listitem>
<para>
PVM a grandi dans le monde de la collecte des cycles machines
inutilisés sur les stations de travail, et sait donc gérer donc
directement les mélanges hétérogènes de machines et de systèmes
d'exploitation. A contrario, MPI part du principe général que
la cible est un MPP (<quote><foreignphrase>Massively Parallel Processor</foreignphrase></quote>, soit
<quote>Processeur Massivement Parallèle</quote>) ou un <foreignphrase>cluster</foreignphrase> dédié
de stations de travail pratiquement toutes identiques.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Syndrome de l'évier.</term>
<listitem>
<para>
PVM se révèle être conçu pour une catégorie d'utilisation bien
définie, ce que MPI&nbsp;2.0 ne fait pas. Le nouveau standard MPI&nbsp;2.0
inclut une variété de fonctionnalités qui s'étendent bien au delà
du simple modèle de <foreignphrase>message-passing</foreignphrase>, comme le RMA
(<quote><foreignphrase>Remote Memory Access</foreignphrase></quote>, soit <quote>Accès Mémoire à Distance</quote>) ou les
opérations d'entrée/sortie en parallèle sur les fichiers. Toutes
ces choses sont-elles bien utiles&nbsp;? Assurément&hellip; mais assimiler
MPI 2.0 est comparable à réapprendre depuis zéro un langage de
programmation totalement nouveau.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Conception de l'interface utilisateur.</term>
<listitem>
<para>
MPI a été conçu après PVM, et en a incontestablement tiré les leçons.
MPI offre une gestion des tampons plus simple et plus efficace et
une couche d'abstraction de haut-niveau permettant de transmettre
des données définies par l'utilisateur comme des messages.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Force de loi.</term>
<listitem>
<para>
Pour ce que j'ai pu en voir, il existe toujours plus d'applications
conçues autour de PVM qu'autour de MPI. Néanmoins, porter celles-ci
vers MPI est chose facile, et le fait que MPI soit soutenu par un
standard formel très répandu signifie que MPI est, pour un certain nombre
d'institutions, une question de vision des choses.
</para>
</listitem>
</varlistentry>
</variablelist>

<para>
Conclusion&nbsp;? Disons qu'il existe au moins trois versions de MPI
développées de façon indépendante et disponibles gratuitement pouvant
fonctionner sur des <foreignphrase>clusters</foreignphrase> de machines Linux (et j'ai écrit l'un
d'eux)&nbsp;:
</para>

<itemizedlist>
<listitem>

<para>
LAM (<quote><foreignphrase>Local Area Multicomputer</foreignphrase></quote>, soit <quote>MultiOrdinateur Local</quote>)
est une mise en &oelig;uvre complète du standard 1.1. Il permet aux programmes
MPI de s'exécuter sur un système Linux individuel ou au travers d'un
<foreignphrase>cluster</foreignphrase> de systèmes Linux communiquant par le biais de <foreignphrase>sockets</foreignphrase> TCP/UDP.
Le système inclut des facilités de base de contrôle de l'exécution, ainsi que
toute une gamme d'outils de développement et de débogage de programmes.
</para>
</listitem>

<listitem><para>

MPICH (<quote><foreignphrase>MPI CHameleon</foreignphrase></quote>) est 
conçu pour être une implémentation complète et hautement portable du 
standard MPI 1.1. Tout comme LAM, il permet aux programmes MPI d'être 
exécutés sur des systèmes Linux individuels ou en 
<foreignphrase>clusters</foreignphrase> via une communication par 
<foreignphrase>socket</foreignphrase> TCP/UDP. En revanche, l'accent est 
porté sur la promotion de MPI en fournissant une implémentation efficace 
et facilement repositionnable. Pour porter cette implémentation, il faut 
réimplémenter soit les cinq fonctions de la <quote>channel 
interface</quote>, soit, pour de meilleures performances, la totalité de 
l'ADI (<quote><foreignphrase>Abstract Device 
Interface</foreignphrase></quote>, soit <quote>Interface Périphérique 
Abstraite</quote>). MPICH, et beaucoup d'informations concernant ce 
sujet et la façon de le porter, sont disponibles sur <ulink 
url="http://www.mcs.anl.gov/mpi/mpich/"/>.

</para></listitem>

<listitem><para>

AFMPI (<quote><foreignphrase>Aggregate Function 
MPI</foreignphrase></quote>) est une sous-implémentation du standard MPI 
2.0. C'est celle que j'ai écrite. S'appuyant sur AFAPI, elle est conçue 
pour être la vitrine des RMA et des fonctions de communications 
collectives, et n'offre donc qu'un soutien minimal des types MPI, de ses 
systèmes de communication, et cætera. Elle permet à des programmes C 
utilisant MPI d'être exécutés sur un système Linux individuel ou au 
travers d'un <foreignphrase>cluster</foreignphrase> mis en réseau par du 
matériel pouvant prendre en charge l'AFAPI.

</para></listitem>

</itemizedlist>

<para>
Quelque soit l'implémentation MPI utilisée, il est toujours très simple
d'effectuer la plupart des types de communication.
</para>

<para>
En revanche, MPI 2.0 incorpore plusieurs paradigmes de communication
suffisamment différents entre eux fondamentalement pour qu'un programmeur
utilisant l'un d'entre eux puisse ne même pas reconnaître les autres
comme étant des styles de programmation MPI. Aussi, plutôt que d'explorer
un seul exemple de programme, il est utile de passer en revue un exemple de chacun
des (différents) paradigmes de communication de MPI. Tous les programmes
qui suivent emploient le même algorithme (celui de la section 1.3)
utilisé pour calculer Pi.
</para>

<para>
Le premier programme MPI utilise les appels de <foreignphrase>message-passing</foreignphrase> MPI sur
chaque processeur pour que celui-ci renvoie son résultat partiel au
processeur 0, qui fait la somme de tous ces résultats et la renvoie à
l'écran&nbsp;:
</para>

<para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;
#include &#60;mpi.h&#62;

main(int argc, char **argv)
{
  register double largeur;
  double somme, sommelocale;
  register int intervalles, i;
  int nproc, iproc;
  MPI_Status status;

  if (MPI_Init(&amp;argc, &amp;argv) != MPI_SUCCESS) exit(1);
  MPI_Comm_size(MPI_COMM_WORLD, &amp;nproc);
  MPI_Comm_rank(MPI_COMM_WORLD, &amp;iproc);
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;
  sommelocale = 0;
  for (i=iproc; i&lt;intervalles; i+=nproc) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;
  if (iproc != 0) {
    MPI_Send(&amp;lbuf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
  } else {
    somme = sommelocale;
    for (i=1; i&#60;nproc; ++i) {
      MPI_Recv(&amp;lbuf, 1, MPI_DOUBLE, MPI_ANY_SOURCE,
               MPI_ANY_TAG, MPI_COMM_WORLD, &amp;status);
      somme += sommelocale;
    }
    printf("Estimation de la valeur de pi: %f\n", somme);
  }
  MPI_Finalize();
  return(0);
}
</programlisting>

</para>

<para>
Le second programme MPI utilise les communications collectives (qui,
pour ce cas précis, sont incontestablement les plus appropriées)&nbsp;:
</para>

<para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;
#include &#60;mpi.h&#62;

main(int argc, char **argv)
{
  register double largeur;
  double somme, sommelocale;
  register int intervalles, i;
  int nproc, iproc;

  if (MPI_Init(&amp;argc, &amp;argv) != MPI_SUCCESS) exit(1);
  MPI_Comm_size(MPI_COMM_WORLD, &amp;nproc);
  MPI_Comm_rank(MPI_COMM_WORLD, &amp;iproc);
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;
  sommelocale = 0;
  for (i=iproc; i&#60;intervalles; i+=nproc) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;
  MPI_Reduce(&amp;sommelocale, &amp;somme, 1, MPI_DOUBLE,
             MPI_SUM, 0, MPI_COMM_WORLD);
  if (iproc == 0) {
    printf("Estimation de la valeur de pi: %f\n", somme);
  }
  MPI_Finalize();
  return(0);
}
</programlisting>

</para>

<para>
La troisième version MPI utilise le mécanisme RMA de MPI&nbsp;2.0 sur chaque processeur
pour ajouter la valeur locale de la variable <literal>sommelocale</literal> de ce dernier
à la variable <literal>somme</literal> du processeur 0&nbsp;:
</para>

<para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;
#include &#60;mpi.h&#62;

main(int argc, char **argv)
{
  register double largeur;
  double somme = 0, sommelocale;
  register int intervalles, i;
  int nproc, iproc;
  MPI_Win somme_fen;

  if (MPI_Init(&amp;argc, &amp;argv) != MPI_SUCCESS) exit(1);
  MPI_Comm_size(MPI_COMM_WORLD, &amp;nproc);
  MPI_Comm_rank(MPI_COMM_WORLD, &amp;iproc);
  MPI_Win_create(&amp;somme, sizeof(somme), sizeof(somme),
                 0, MPI_COMM_WORLD, &amp;somme_fen);
  MPI_Win_fence(0, somme_fen);
  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;
  sommelocale = 0;
  for (i=iproc; i&#60;intervalles; i+=nproc) {
    register double x = (i + 0.5) * largeur;
    sommelocale += 4.0 / (1.0 + x * x);
  }
  sommelocale *= largeur;
  MPI_Accumulate(&amp;sommelocale, 1, MPI_DOUBLE, 0, 0,
                 1, MPI_DOUBLE, MPI_SUM, somme_fen);
  MPI_Win_fence(0, somme_fen);
  if (iproc == 0) {
    printf("Estimation de la valeur de pi: %f\n", somme);
  }
  MPI_Finalize();
  return(0);
}
</programlisting>

</para>

<para>
Il est utile de préciser que le mécanisme RMA de MPI 2.0 prévient
de façon remarquable tout problème de structures de données se trouvant
à des adresses mémoires différentes selon les processeurs, en se référant
à une "fenêtre" incluant l'adresse de base, une protection contre les
accès mémoire hors de portée, et même le rééchelonnement d'adresse. À une
implémentation efficace, s'ajoute le fait qu'un traitement RMA peut-être
reporté jusqu'à la prochaine <literal>MPI__Win_fence</literal>.
Pour faire simple, le mécanisme RMA est un étrange croisement entre
mémoire partagée distribuée et <foreignphrase>message passing</foreignphrase>,
mais reste une interface très propre pouvant générer des communications très efficaces.
</para>

</sect2>

<sect2>
<title>AFAPI (<quote><foreignphrase>Aggregate Function API</foreignphrase></quote>)</title>

<para>

Contrairement à PVM, MPI, et cætera, l'interface AFAPI 
(<quote><foreignphrase>Aggregate Function API</foreignphrase></quote>, 
ou <quote>Interface à Fonctions d'Agrégation</quote>) n'a pas débuté sa 
vie en tant que couche d'abstraction portable s'appuyant sur un réseau 
matériel ou logiciel existant. AFAPI était plutôt la bibliothèque de 
gestion bas niveau d'un matériel spécifique pour PAPERS 
(<quote><foreignphrase>Purdue's Adapter for Parallel Execution and Rapid 
Synchronization</foreignphrase></quote>, soit <quote>Adaptateur pour 
l'Exécution en Parallèle et la Synchronisation Rapide de l'université de 
Purdue</quote>).

</para>

<para>
PAPERS a été rapidement présenté dans la section 3.2. Il s'agit d'un
réseau à fonction d'agrégations conçu sur mesure dont le modèle est dans
domaine public et qui présente des temps de latence inférieurs à quelques
microsecondes. Mais surtout, il s'agit de la tentative de construction
d'un supercalculateur formant une meilleure cible pour la technologie des
compilateurs que les supercalculateurs déjà existants. Il se distingue
en qualité de la plupart des efforts en matière de <foreignphrase>clusters</foreignphrase> Linux et de
PVM/MPI, qui s'attachent généralement à essayer d'exploiter les réseaux
standard au profit des rares applications en parallèle présentant une
granularité suffisante. Le fait que les éléments de PAPERS soient des
machines PC sous Linux ne sert qu'à permettre l'implémentation de
prototypes à des coûts les plus avantageux possibles.
</para>

<para>
La nécessité d'avoir une interface logicielle de bas niveau commune à
plus d'une douzaine d'implémentations différentes d'un prototype a
conduit la bibliothèque PAPERS à être standardisée sous le nom d'AFAPI.
Mais le modèle utilisé par AFAPI est simple en lui-même et bien plus
adapté aux interactions à la granularité plus fine, typiquement du code
compilé par des compilateurs parallélisés, ou écrit pour des architectures
SIMD. Non seulement la simplicité du modèle rend les machines PAPERS
aisées à construire, mais elle apporte également une efficacité surprenante
aux ports d'AFAPI sur différents types de système, tels que les SMP.
</para>

<para>
AFAPI fonctionne actuellement sur des <foreignphrase>clusters</foreignphrase> Linux utilisant TTL_PAPERS,
CAPERS ou WAPERS. Elle fonctionne également (sans appel système ni même
instruction de verrouillage de bus, voir la section 2.2) sur les machines
SMP utilisant une bibliothèque de gestion de mémoire partagée type System V
(<quote><foreignphrase>System V Shared Memory</foreignphrase></quote>) appelée SHMAPERS. Une version fonctionnant sur
des <foreignphrase>clusters</foreignphrase> Linux utilisant la diffusion UDP sur des réseaux conventionnels
(Ex&nbsp;: Ethernet) est en cours de développement. Toutes les versions d'AFAPI sont écrites
pour être appelées à partir des langages C ou C++.
</para>

<para>
L'exemple suivant est la version AFAPI du programme de calcul de
Pi décrit dans la section 1.3.
</para>

<para>

<programlisting>
#include &#60;stdlib.h&#62;
#include &#60;stdio.h&#62;
#include "afapi.h"

main(int argc, char **argv)
{
  register double largeur, somme;
  register int intervalles, i;

  if (p_init()) exit(1);

  intervalles = atoi(argv[1]);
  largeur = 1.0 / intervalles;

  sum = 0;
  for (i=IPROC; i&#60;intervalles; i+=NPROC) {
    register double x = (i + 0.5) * largeur;
    somme += 4.0 / (1.0 + x * x);
  }

  somme = p_reduceAdd64f(somme) * largeur;

  if (IPROC == CPROC) {
    printf("Estimation de la valeur de pi: %f\n", somme);
  }

  p_exit();
  return(0);
}
</programlisting>

</para>

</sect2>

<sect2>
<title>Autres bibliothèques de gestion de <foreignphrase>clusters</foreignphrase></title>

<para>

Outre PVM, MPI et AFAPI, les bibliothèques suivantes proposent des 
services qui peuvent s'avérer utiles au travers de grappes de machines 
Linux. Ces systèmes sont traités ici de manière moins approfondie 
simplement parce que, contrairement à PVM, MPI et AFAPI, je n'ai que 
peu, voire aucune expérience pratique de l'utilisation de ceux-ci sur 
des <foreignphrase>clusters</foreignphrase> Linux. Si l'une de ces 
bibliothèques (ou même d'autres) vous est particulièrement utile, merci 
de m'envoyer un courrier électronique en anglais à

<email>&addr-auteur;</email>

en me détaillant vos découvertes. J'envisagerai alors d'ajouter une 
section plus complète à son sujet.

</para>

<sect3>
<title>Condor (migration de processus)</title>

<para>

Condor est un système de gestion de ressources distribuées qui peut 
diriger de vastes <foreignphrase>clusters</foreignphrase> de stations de 
travail hétérogènes. Sa conception a été motivée par les besoins des 
utilisateurs souhaitant utiliser la puissance inexploitée de tels 
<foreignphrase>clusters</foreignphrase> au profit de leurs tâches aux 
temps d'exécution prolongés et aux calculs intensifs. Condor reproduit 
dans une large mesure l'environnement de la machine initiale sur celle 
qui exécute le processus, même si ces deux machines ne partagent pas un 
système de fichier ou un mécanisme de mot de passe communs. Les tâches 
sous Condor qui se résument à un processus unique sont automatiquement 
interceptées et déplacées entre les différentes stations en fonctions 
des besoins pour les mener à terme.

</para>

<para>

Condor est disponible sur <ulink url="http://www.cs.wisc.edu/condor/"/>. 
Une version Linux existe également. Contactez l'administrateur du site,

<email>condor TIRET admin CHEZ cs POINT wisc POINT edu</email>,

pour plus de détails.

</para>

</sect3>

<sect3>
<title>DFN-RPC (Réseau Allemand de la Recherche &mdash; <quote><foreignphrase>Remote Procedure Call</foreignphrase></quote>)</title>

<para>

Le DFN-RPC (un outil du Réseau Allemand de la Recherche &mdash; 
<quote><foreignphrase>Remote Procedure Call</foreignphrase></quote>) a 
été développé pour distribuer et paralléliser des applications d'intéret 
scientifique ou technique entre une station de travail et un serveur de 
calcul ou un <foreignphrase>cluster</foreignphrase>. L'interface est 
optimisée pour les applications écrites en Fortran, mais le DFN-RPC peut 
aussi être utilisé dans un environnement de langage C. Une version Linux 
a été écrite. Plus d'information sur <ulink 
url="ftp://ftp.uni-stuttgart.de/pub/rus/dfn_rpc/README_dfnrpc.html"/>.

</para>

</sect3>

<sect3>
<title>DQS (<quote><foreignphrase>Distributed Queueing System</foreignphrase></quote>)</title>

<para>

Pas vraiment une bibliothèque, DQS 3.0 (<quote>Distributed Queueing 
System</quote>, soit <quote>Système de Files d'attente 
Distribuées</quote>) est un système de mise en file d'attente des tâches 
qui a été développé et testé sous Linux. Ce système a été conçu pour 
permettre à la fois l'utilisation et l'administration d'un 
<foreignphrase>cluster</foreignphrase> de machines hétérogènes comme une 
seule entité. Disponible sur <ulink 
url="http://www.scri.fsu.edu/~pasko/dqs.html"/>.

</para>

<para>
Il existe aussi une version commerciale nommée CODINE 4.1.1 (<quote><foreignphrase>COmputing in
DIstributed Network Environments</foreignphrase></quote>, soit <quote>CAlcul en Environnement Réseau Distribué</quote>).
</para>

</sect3>

</sect2>

<sect2>
<title>Références générales aux <foreignphrase>clusters</foreignphrase></title>

<para>
Les <foreignphrase>clusters</foreignphrase> peuvent être construits et utilisés de tellement
de manières différentes que certains groupes ont apporté des
contributions particulièrement intéressantes. Ce qui suit fait référence aux
différents projets liés à la mise en place de <foreignphrase>clusters</foreignphrase> pouvant
avoir un intérêt d'ordre général. Ceci inclut un mélange de
références à des <foreignphrase>clusters</foreignphrase> spécifiques à Linux et à des <foreignphrase>clusters</foreignphrase>
génériques. Cette liste est présentée dans l'ordre alphabétique.
</para>

<sect3>
<title>Beowulf</title>

<para>

Le projet <ulink url="http://www.beowulf.org/">Beowulf</ulink>, se 
focalise sur la production de logiciels pour une utilisation de stations 
de travail immédiatement disponibles basée sur du matériel PC de grande 
distribution, un réseau à haut débit interne au 
<foreignphrase>cluster</foreignphrase>, et le système d'exploitation 
Linux.

</para>

<para>
Thomas Sterling a été le principal acteur de Beowulf, et continue
d'être un promoteur franc et éloquent de l'utilisation de <foreignphrase>clusters</foreignphrase>
Linux dans le domaine du calcul scientifique en général. À vrai dire,
plusieurs groupes parlent à présent de leur <foreignphrase>cluster</foreignphrase> comme de système
de <quote>classe Beowulf</quote>, et ce même si la conception de ce <foreignphrase>cluster</foreignphrase>
s'éloigne du modèle Beowulf officiel.
</para>

<para>
Don Becker, apportant son appui au projet Beowulf, a produit nombre
des pilotes réseau utilisés par Linux en général. Plusieurs de ces
pilotes ont même été adaptés pour être utilisés sous BSD. C'est
également à Don que l'on doit la possibilité, pour certains pilotes,
de répartir le trafic réseau à travers plusieurs connexions parallèles
pour augmenter les taux de transfert sans utiliser d'onéreux commutateurs.
Ce type de répartition de la charge réseau était le principal atout des
<foreignphrase>clusters</foreignphrase> Beowulf.

</para>

</sect3>

<sect3>
<title>Linux/AP+</title>

<para>

Le projet <ulink 
url="http://cap.anu.edu.au/cap/projects/linux">Linux/AP+</ulink> ne 
concerne pas exactement le <emphasis>clustering</emphasis> sous Linux, 
mais s'attache à faire fonctionner Linux sur l'AP1000+ de Fujitsu, et à 
y apporter les améliorations appropriées en matière de traitement en 
parallèle. L'AP1000+ est une machine en parallèle à base de SPARC et 
disponible dans le commerce, utilisant un réseau spécifique avec une 
topologie en tore, un taux de transfert de 25Mo/s et un temps de latence 
de 10 microsecondes&hellip; Pour faire court, cela ressemble beaucoup à 
un <foreignphrase>cluster</foreignphrase> Linux SPARC.

</para>

</sect3>

<sect3>
<title>Locust</title>

<para>
Le projet Locust est en train de mettre au point un système de mémoire partagée
virtuelle qui utilise les informations obtenues à la compilation pour
masquer les temps de latence des messages et réduire le trafic réseau
lors de l'exécution. <quote>Pupa</quote> forme la base du système de communication de
Locust, et est implémenté à l'aide d'un réseau Ethernet reliant des
machines PC 486 sous FreeBSD. Et Linux&nbsp;?
</para>

</sect3>

<sect3>
<title>Midway DSM (<quote><foreignphrase>Distributed Shared Memory</foreignphrase></quote>)</title>

<para>
<ulink
url="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/midway/WWW/HomePage.html"
>Midway</ulink> est une DSM (<quote><foreignphrase>Distributed Shared Memory</foreignphrase></quote>,
soit <quote>Mémoire Partagée Distribuée</quote>) logicielle, similaire à TreadMarks.
Le bon coté réside en l'utilisation d'indications à la compilation plutôt que de relativement
lents mécanismes d'erreur de page, et en sa gratuité. Le mauvais coté est cela ne fonctionne
pas sur des <foreignphrase>clusters</foreignphrase> Linux.
</para>

</sect3>

<sect3>
<title>Mosix</title>

<para>

MOSIX apporte des modifications au système d'exploitation BSD 
<quote>BSDI</quote> pour proposer une répartition de charge réseau 
(<quote><foreignphrase>load balancing</foreignphrase></quote>) dynamique 
et une migration de processus préemptive au travers d'un groupe de PC 
mis en réseau. C'est un système très utile non seulement pour le 
traitement en parallèle, mais d'une manière générale pour utiliser un 
<foreignphrase>cluster</foreignphrase> comme une machine SMP évolutive. 
Y aura-t-il une version Linux&nbsp;? Voyez <ulink 
url="http://www.mosix.org"/> pour plus d'informations<footnote><para>

N.D.T.&nbsp;: MOSIX fonctionne aujourd'hui sous Linux.

</para></footnote>.

</para></sect3>

<sect3>
<title>NOW (<quote><foreignphrase>Network Of Workstations</foreignphrase></quote>)</title>

<para>
Le projet NOW (<quote><foreignphrase>Network Of Workstations</foreignphrase></quote>, ou <quote>Réseau de Stations de Travail</quote>) de l'université de Berkeley (<ulink
url="http://now.cs.berkeley.edu/"
>http://now.cs.berkeley.edu</ulink
>) a conduit dans une large mesure l'effort pour le calcul en
parallèle en utilisant des réseaux de stations de travail. Bon
nombre de travaux sont menés là-bas, tous tournés vers la
<quote>démonstration en pratique d'un système à 100 processeurs dans
les prochaines années</quote>. Hélas, ils n'utilisent pas Linux.
</para>

</sect3>

<sect3>
<title>Traitement en parallèle avec Linux</title>

<para>
Le site web du <quote>Traitement en parallèle avec Linux</quote>
(<quote><foreignphrase>Parallel processing using Linux</foreignphrase></quote>), sur <ulink
url="http://yara.ecn.purdue.edu/~pplinux"
>http://yara.ecn.purdue.edu/~pplinux</ulink>>, est le site officiel
de ce guide pratique et de plusieurs documents en rapport avec ce thème,
y compris des présentations en ligne.
Parallèlement aux travaux du projet PAPERS, l'École Supérieure
d'Électricité et d'Informatique de Purdue (<quote>Purdue
University School of Electrical and Computer Engineering</quote>) reste
un leader en matière de traitement en parallèle. Ce site a été
mis en place pour aider les autres à utiliser des PC sous Linux
pour faire du traitement en parallèle.
</para>

<para>
Depuis l'assemblage du premier <foreignphrase>cluster</foreignphrase> de PC Linux en février 1994,
bien d'autres furent également assemblés à Purdue, dont plusieurs
équipés de murs vidéos. Bien que ces <foreignphrase>clusters</foreignphrase> s'appuyaient sur des
machines à base de microprocesseurs 386, 486 ou Pentium (mais pas de
Pentium Pro), Intel a récemment accordé à Purdue une donation qui lui
permettra de construire plusieurs grands <foreignphrase>clusters</foreignphrase> de systèmes à
Pentium II (avec pas moins de 165 machines par <foreignphrase>cluster</foreignphrase>). Même si
tous ces <foreignphrase>clusters</foreignphrase> sont ou seront équipés de réseaux PAPERS, la plupart
sont également dotés de réseaux conventionnels.
</para>

</sect3>

<sect3>
<title>Pentium Pro Cluster Workshop</title>

<para>
Les 10 et 11 avril 1997, le laboratoire AMES a tenu à Des Moines,
dans l'état de l'Iowa aux États-Unis, le <quote><foreignphrase>Pentium Pro Cluster
Workshop</foreignphrase></quote> (<quote>Atelier de <foreignphrase>clusters</foreignphrase> Pentium Pro</quote>). Le site web
de cet atelier, <ulink
url="http://www.scl.ameslab.gov"
>http://www.scl.ameslab.gov</ulink
>, renferme une mine d'informations concernant les <foreignphrase>clusters</foreignphrase> PC,
glanées auprès de tous les participants.
</para>

</sect3>

<sect3>
<title>TreadMarks DSM (<quote><foreignphrase>Distributed Shared Memory</foreignphrase></quote>)</title>

<para>

La DSM (<quote><foreignphrase>Distributed Shared 
Memory</foreignphrase></quote>, ou <quote>Mémoire Partagée 
Distribuée</quote>) est une technique avec laquelle un système de 
<foreignphrase>message-passing</foreignphrase> peut se présenter et agir 
comme un SMP. Il existe quelques systèmes de ce genre, la plupart 
utilisant les mécanismes d'erreur de page du système d'exploitation pour 
déclencher la transmission des messages. <ulink 
url="http://www.cs.rice.edu/~willy/TreadMarks/overview.html">TreadMarks</ulink> 
est l'un des plus efficaces, et fonctionne sur les 
<foreignphrase>clusters</foreignphrase> Linux. La mauvaise nouvelle est 
que <quote>TreadMarks est distribué à un coût réduit aux universités et 
organisations à but non lucratif</quote>. Pour plus d'informations 
concernant le logiciel, prenez contact (en anglais) avec

<email>tmk CHEZ cs POINT rice POINT edu</email>.
</para>

</sect3>

<sect3>
<title>U-Net (<quote><foreignphrase>User-level NETwork interface architecture</foreignphrase></quote>)</title>

<para>

Le projet U-Net (<quote><foreignphrase>User-level NETwork interface 
architecture</foreignphrase></quote>, ou <quote>Architecture d'interface 
Réseau au Niveau Utilisateur</quote>), accessible sur <ulink 
url="http://www.eecs.harvard.edu/~mdw/proj/old/unet"/>, tente d'apporter 
temps de latence réduits et taux de transfert élevés sur du matériel 
réseau du commerce en virtualisant les interfaces réseau de manière à ce 
que les applications puissent envoyer et recevoir des messages sans 
passer par un appel système. U-Net fonctionne sur des PC Linux en 
utilisant du matériel Fast Ethernet basé sur une puce DEC DC21140, ou 
une carte ATM Fore Systems PCA-200 (mais pas PCA-200E).

</para>

</sect3>

<sect3>
<title>WWT (<quote><foreignphrase>Wisconsin Wind Tunnel</foreignphrase></quote>)</title>

<para>

On trouve bon nombre de projets relatifs à l'utilisation de 
<foreignphrase>clusters</foreignphrase> dans le Wisconsin. Le projet WWT 
(<quote><foreignphrase>Wisconsin Wind Tunnel</foreignphrase></quote>, ou 
<quote>Soufflerie du Wisconsin</quote>), sur <ulink 
url="http://www.cs.wisc.edu/~wwt/"/>, mène toutes sortes de travaux 
orientés vers le développement d'une interface <quote>standard</quote> 
entre les compilateurs et le matériel réseau sur lequel ils s'appuient. 
Il existe le <quote><foreignphrase>Wisconsin COW</foreignphrase></quote> 
(pour <quote><foreignphrase>Cluster Of 
Workstation</foreignphrase></quote>), <quote><foreignphrase>Cooperative 
Shared Memory</foreignphrase></quote> et 
<quote><foreignphrase>Tempest</foreignphrase></quote>, le 
<quote><foreignphrase>Paradyn Parallel Performance 
Tools</foreignphrase></quote>, et cætera. Malheureusement, il n'y a pas 
grand chose concernant Linux.

</para>

</sect3>

</sect2>

</sect1>

<sect1>
<title>SIMD <foreignphrase>Within A Register</foreignphrase>&nbsp;: SWAR (Ex&nbsp;: utilisation de MMX)</title>

<para>

Le SIMD (<quote><foreignphrase>Single Instruction stream, Multiple Data 
stream</foreignphrase></quote> ou <quote>Un seul flux d'instruction, 
plusieurs flux de données</quote>) à l'Intérieur d'Un Registre (ou 
<quote>SIMD <foreignphrase>Within A 
Register</foreignphrase></quote>&nbsp;: SWAR) n'est pas un concept 
récent. En considérant une machine dotée de registres, bus de données et 
unités de fonctions de <emphasis>k</emphasis> bits, il est connu depuis 
longtemps que les opérations sur les registres ordinaires peuvent se 
comporter comme des opérations parallèles SIMD sur 
<emphasis>n</emphasis> champs de 
<emphasis>k</emphasis>/<emphasis>n</emphasis> bits chacun. Ce n'est en 
revanche qu'avec les efforts récents en matière de multimédia que 
l'accélération d'un facteur deux à huit apportée par les techniques SWAR 
a commencé à concerner l'informatique générale. Les versions de 1997 de 
la plupart des microprocesseurs incorporent une prise en charge 
matérielle du SWAR<footnote><para>

N.D.T.&nbsp;: initialement, huit liens étaient proposés à cet endroit, 
devenus pour la plupart obsolètes.

</para></footnote>&nbsp;:

</para>

<para>

<itemizedlist>
<listitem>

<para>
<ulink
url="http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/20726.pdf"
>AMD K6 MMX (<quote><foreignphrase>MultiMedia eXtensions</foreignphrase></quote>)</ulink
>

</para>
</listitem>
<listitem>

<para>
<ulink
url="http://www.sun.com/sparc/vis/index.html"
>Sun SPARC V9 VIS (<quote><foreignphrase>Visual Instruction Set</foreignphrase></quote>)</ulink
>
</para>
</listitem>

</itemizedlist>

</para>

<para>
Il existe quelques lacunes dans la prise en charge matérielle
apportée par les nouveaux microprocesseurs, des caprices comme
par exemple la prise en charge d'un nombre limité d'instructions
pour certaines tailles de champ. Il est toutefois important de
garder à l'esprit que bon nombre d'opérations SWAR peuvent se
passer d'accélération matérielle. Par exemple, les opérations
au niveau du bit sont insensibles au partitionnement logique d'un
registre.
</para>

<sect2>
<title>Quels usages pour le SWAR&nbsp;?</title>

<para>
Bien que <emphasis>tout</emphasis> processeur moderne soit capable
d'exécuter un programme en effectuant un minimum de parallélisme
SWAR, il est de fait que même le plus optimisé des jeux d'instructions
SWAR ne peut gérer un parallélisme d'intérêt vraiment général. A dire
vrai, de nombreuses personnes ont remarqué que les différences de
performance entre un Pentium ordinaire et un Pentium <quote>avec technologie MMX</quote>
étaient surtout dûes à certains détails, comme le fait que l'augmentation
de la taille du cache L1 coïncide avec l'apparition du MMX. Alors,
concrètement, à quoi le SWAR (ou le MMX) est-il utile&nbsp;?
</para>

<itemizedlist>

<listitem><para>

Dans le traitement des entiers, les plus courts étant les meilleurs. 
Deux valeurs 32 bits tiennent dans un registre MMX 64 bits, mais forment 
aussi une chaîne de huit caractères, ou encore permettent de représenter un
échiquier complet, à raison d'un bit par case. Note&nbsp;: il 
<emphasis>existera une version <quote>virgule flottante</quote> du 
MMX</emphasis>, bien que très peu de choses aient été dites à ce sujet. 
Cyrix a déposé une présentation, <ulink 
url="ftp://ftp.cyrix.com/developr/mpf97rm.pdf"/>, qui contient quelques 
commentaires concernant <emphasis>MMFP</emphasis>. Apparemment, MMFP 
pourra prendre en charge le chargement de nombres 32 bits en virgule 
flottante dans des registres MMX 64 bits. Ceci combiné à deux pipelines 
MMFP fournirait quatre FLOP <footnote><para>

N.D.T.&nbsp;: FLOP = <quote><foreignphrase>FLoating point 
OPeration</foreignphrase></quote>, ou <quote>OPération en Virgule 
Flottante</quote>.

</para></footnote> en simple précision par cycle d'horloge.

</para></listitem>

<listitem><para>

Pour le SIMD, ou parallélisme vectorisé. La même opération s'applique à 
tous les champs, simultanément. Il existe des moyens d'annihiler ses 
effets sur des champs sélectionnés (l'équivalent du 
<quote><foreignphrase>SIMD enable masking</foreignphrase></quote>, ou 
<quote>activation du masquage</quote>), mais ils sont compliqués à 
mettre en &oelig;uvre et grèvent les performances.

</para></listitem>
<listitem><para>

Pour les cas où la référence à la mémoire se fait de manière localisée 
et régulière (et de préférence regroupée). Le SWAR en général, et le MMX 
en particulier se révèlent désastreux sur les accès aléatoires. 
Rassembler le contenu d'un vecteur <literal>x[y]</literal> (où 
<literal>y</literal> est l'index d'un tableau) est extrêmement coûteux.

</para></listitem>

</itemizedlist>

<para>
Il existe donc d'importantes limitations, mais ce type de parallélisme
intervient dans un certain nombre d'algorithmes, et pas seulement dans les
applications multimédia. Lorsque l'algorithme est adapté, le SWAR est plus
efficace que le SMP ou le parallélisme en <foreignphrase>clusters</foreignphrase>&hellip; et son utilisation
ne coûte rien&nbsp;!
</para>

</sect2>

<sect2>
<title>Introduction à la programmation SWAR</title>

<para>
Le concept de base du SWAR (<quote><foreignphrase>SIMD Within A Register</foreignphrase></quote>,
ou <quote>SIMD à l'intérieur d'un registre</quote>) réside dans le fait que l'on peut utiliser des opérations sur
des registres de la taille d'un mot pour accélérer les calculs en effectuant des
opérations parallèles SIMD sur <emphasis>n</emphasis> champs
de <emphasis>k</emphasis>/<emphasis>n</emphasis> bits. En revanche,
employer les techniques du SWAR peut parfois s'avérer maladroit, et certaines de leurs
opérations peuvent au final être plus coûteuses que leurs homologues en série
car elles nécessitent des instructions supplémentaires pour forcer le partitionnement
des champs.
</para>

<para>
Pour illustrer ce point, prenons l'exemple d'un mécanisme SWAR
grandement simplifié gérant quatre champs de 8 bits dans chaque
registre de 32 bits. Les valeurs de ces deux registres pourraient
être représentées comme suit&nbsp;:
</para>

<para>

<programlisting>
         PE3     PE2     PE1     PE0
      +-------+-------+-------+-------+
Reg0  | D 7:0 | C 7:0 | B 7:0 | A 7:0 |
      +-------+-------+-------+-------+
Reg1  | H 7:0 | G 7:0 | F 7:0 | E 7:0 |
      +-------+-------+-------+-------+
</programlisting>

</para>

<para>

Ceci indique simplement que chaque registre est vu essentiellement comme 
un vecteur de quatre valeurs entières 8 bits indépendantes. 
Alternativement, les valeurs <literal>A</literal> et 
<literal>E</literal> peuvent être vues comme les valeurs, dans Reg0 et 
Reg1, de l'élément de traitement 0 (ou <quote>PE0</quote> pour 
<quote><foreignphrase>Processing Element 0</foreignphrase></quote>), 
<literal>B</literal> et <literal>F</literal> comme celles de l'élément 
1, et ainsi de suite.

</para>

<para>
Le reste de ce document passe rapidement en revue les classes de
base des opérations parallèles SIMD sur ces vecteurs d'entiers et
la façon dont ces fonctions peuvent être implémentées.
</para>

<sect3>
<title>Opérations polymorphiques</title>

<para>
Certaines opérations SWAR peuvent être effectuées de façon triviale
en utilisant des opérations ordinaires sur des entiers 32 bits, sans
avoir à se demander si l'opération est réellement faite pour agir en
parallèle et indépendemment sur ces champs de 8 bits. On qualifie ce
genre d'opération SWAR de <emphasis>polymorphique</emphasis>, parce que
la fonction est insensible aux types des champs (et à leur taille).
</para>

<para>
Tester si un champ quelconque est non-nul est une opération polymorphique,
tout comme les opérations logiques au niveau du bit. Par exemple, un <quote>ET</quote>
logique bit-à-bit ordinaire (l'opérateur <quote><literal>&amp;</literal></quote>
du registre C) effectue son calcul bit-à-bit, quelque soit la taille des champs.
Un <quote>ET</quote> logique simple des registres ci-dessus donnerait&nbsp;:
</para>

<para>

<programlisting>
          PE3       PE2       PE1       PE0
      +---------+---------+---------+---------+
Reg2  | D&amp;H 7:0 | C&amp;G 7:0 | B&amp;F 7:0 | A&amp;E 7:0 |
      +---------+---------+---------+---------+
</programlisting>

</para>

<para>
Puisque le bit de résultat <emphasis>k</emphasis> de l'opération <quote>ET</quote> logique
n'est affecté que par les valeurs des bits d'opérande <emphasis>k</emphasis>,
toutes les tailles de champs peuvent être prises en charge par une même instruction.
</para>

</sect3>

<sect3>
<title>Opérations partitionnées</title>

<para>
Malheureusement, de nombreuses et importantes opérations SWAR ne sont
pas polymorphiques. Les opérations arithmétiques comme l'addition, la
soustraction, la multiplication et la division sont sujettes aux
interactions de la <quote>retenue</quote> entre les champs. Ces opérations sont
dites <emphasis>partitionnées</emphasis> car chacune d'elles doit
cloisonner effectivement les opérandes et le résultat pour éviter les
interactions entre champs. Il existe toutefois trois méthodes différentes
pouvant être employées pour parvenir à ces fins&nbsp;:
</para>

<sect4>
<title>Instructions partitionnées</title>

<para>
L'approche la plus évidente pour implémenter les opérations partitionnées
consiste peut-être à proposer une prise en charge matérielle des
<quote>instructions parallèles partitionnées</quote> coupant le système de retenue
entre les champs. Cette approche peut offrir les performances les plus
élevées, mais elle nécessite la modification du jeu d'instruction du
processeur et implique en général des limitations sur la taille des
champs (Ex&nbsp;: ces instructions pourraient prendre en charge des champs larges
de 8 bits, mais pas de 12).
</para>

<para>
Le MMX des processeurs AMD, Cyrix et Intel, Le MAX de Digital, celui d'HP,
et le VIS de Sun implémentent tous des versions restreintes des instructions
partitionnées. Malheureusement, ces différents jeux d'instructions posent
des restrictions assez différentes entre elles, ce qui rend les algorithmes
difficilement portables. Étudions à titre d'exemple l'échantillon d'opérations
partitionnées suivant&nbsp;:
</para>

<para>

<programlisting>
  Instruction           AMD/Cyrix/Intel MMX   DEC MAX   HP MAX   Sun VIS
+---------------------+---------------------+---------+--------+---------+
| Différence Absolue  |                     |       8 |        |       8 |
+---------------------+---------------------+---------+--------+---------+
| Maximum             |                     |   8, 16 |        |         |
+---------------------+---------------------+---------+--------+---------+
| Comparaison         |           8, 16, 32 |         |        |  16, 32 |
+---------------------+---------------------+---------+--------+---------+
| Multiplication      |                  16 |         |        |    8x16 |
+---------------------+---------------------+---------+--------+---------+
| Addition            |           8, 16, 32 |         |     16 |  16, 32 |
+---------------------+---------------------+---------+--------+---------+
</programlisting>

</para>

<para>
Dans cette table, les chiffres indiquent les tailles de champ reconnues
par chaque opération. Même si la table exclut un certain nombre d'instructions
dont les plus exotiques, il est clair qu'il y a des différences. Cela a pour effet
direct de rendre inefficaces les modèles de programmation en langages de haut niveau,
et de sévèrement restreindre la portabilité.

</para>

</sect4>

<sect4>
<title>Opérations non partitionnées avec code de correction</title>

<para>
Implémenter des opérations partitionnées en utilisant des instructions
partitionnées est certainement très efficace, mais comment faire lorsque
l'opération dont vous avez besoin n'est pas prise en charge par le matériel&nbsp;?
Réponse&nbsp;: utiliser une série d'instructions ordinaires pour effectuer l'opération
malgré les effets de la retenue entre les champs, puis effectuer la correction
de ces effets indésirés.
</para>

<para>
C'est une approche purement logicielle, et les corrections apportent
bien entendu un surcoût en temps, mais elle fonctionne pleinement avec
le partitionnement en général. Cette approche est également <quote>générale</quote>
dans le sens où elle peut être utilisée soit pour combler les lacunes du
matériel dans le domaine des instructions partitionnées, soit pour apporter
un soutien logiciel complet aux machines qui ne sont pas du tout dotées
d'une prise en charge matérielle. À dire vrai, en exprimant ces séquences
de code dans un langage comme le C, on permet au SWAR d'être totalement
portable.
</para>




<para>
Ceci soulève immédiatement une question&nbsp;: quelle est précisément l'inefficacité
des opérations SWAR partitionnées simulées à l'aide d'opérations non partitionnées&nbsp;?
Eh bien c'est très certainement la question à 65536 dollars, mais certaines opérations
ne sont pas aussi difficiles à mettre en &oelig;uvre que l'on pourrait le croire.
</para>

<para>
Prenons en exemple le cas de l'implémentation de l'addition d'un vecteur de quatre
éléments 8 bits contenant des valeurs entières, soit <literal>x</literal>+<literal>y</literal>,
en utilisant des opérations 32 bits ordinaires.
</para>

<para>
Une addition 32 bits ordinaire pourrait en fait rendre un résultat correct,
mais pas si la retenue d'un champ de 8 bits se reporte sur le champ suivant.
Aussi, notre but consiste simplement à faire en sorte qu'un tel report ne se produise
pas. Comme l'on est sûr qu'additionner deux champs de <emphasis>k</emphasis> bits
ne peut générer qu'un résultat large d'au plus <emphasis>k</emphasis>+1 bits,
on peut garantir qu'aucun report ne va se produire en <quote>masquant</quote> simplement le
bit de poids fort de chaque champs. On fait cela en appliquant un <quote>ET</quote> logique
à chaque opérande avec la valeur <literal>0x7f7f7f7f</literal>, puis
en effectuant un addition 32 bits habituelle.
</para>

<para>

<programlisting>
t = ((x &amp; 0x7f7f7f7f) + (y &amp; 0x7f7f7f7f));
</programlisting>

</para>

<para>
Ce résultat est correct&hellip; sauf pour le bit de poids fort de chacun
des champs. Il n'est question, pour calculer la valeur correcte, que
d'effectuer deux additions 1-bit partitionnées, depuis <literal>x</literal>
et <literal>y</literal> vers le résultat à 7 bits calculé
pour <literal>t</literal>. Heureusement, une addition
partitionnée sur 1 bit peut être implémentée à l'aide d'une
opération <quote>OU Exclusif</quote> logique ordinaire. Ainsi, le résultat est
tout simplement&nbsp;:
</para>

<para>

<programlisting>
(t ^ ((x ^ y) &amp; 0x80808080))
</programlisting>

</para>

<para>
D'accord. Peut-être n'est-ce pas si simple, en fin de compte. Après tout, cela fait six
opérations pour seulement quatre additions. En revanche, on remarquera
que ce nombre d'opérations n'est pas fonction du nombre de champs. Donc,
avec plus de champs, on gagne en rapidité. À dire vrai, il se peut que
l'on gagne quand même en vitesse simplement parce que les champs sont
chargés et redéposés en une seule opération (vectorisée sur des entiers),
que la disponibilité des registres est optimisée, et parce qu'il y a
moins de code dynamique engendrant des dépendances (parce que l'on évite
les références à des mots incomplets).
</para>

</sect4>

<sect4>
<title>Contrôler les valeurs des champs</title>

<para>
Alors que les deux autres approches de l'implémentation des opérations
partitionnées se centrent sur l'exploitation du maximum d'espace possible
dans les registres, il peut être, au niveau du calcul, plus efficace de
contrôler les valeurs des champs de façon à ce que l'interaction
de la retenue entre ceux-ci ne se produise jamais. Par exemple, si l'on sait que
toutes les valeurs de champs additionnées sont telles qu'aucun dépassement
ne peut avoir lieu, une addition partitionnée peut être implémentée à
l'aide d'une instruction ordinaire. Cette contrainte posée, une addition
ordinaire pourrait en fin de compte apparaître polymorphique,
et être utilisable avec n'importe quelle taille de champ sans programme de
correction. Ce qui nous amène ainsi à la question suivante&nbsp;: Comment s'assurer
que les valeurs des champs ne vont pas provoquer d'événements dus à la
retenue&nbsp;?
</para>

<para>
Une manière de faire cela consiste à implémenter des instructions partitionnées
capables de restreindre la portée des valeurs des champs. Les instructions
vectorisées de maximum et de minimum du MAX de Digital peuvent être
assimilées à une prise en charge matérielle de l'écrêtage des valeurs des
champs pour éviter les interactions de la retenue entre les champs.
</para>

<para>
En revanche, si l'on ne dispose pas d'instructions partitionnées à même
de restreindre efficacement la portée des valeurs des champs, existe-t-il
une condition qui puisse être imposée à un coût raisonnable et qui soit
suffisante pour garantir le fait que les effets de la retenue n'iront pas
perturber les champs adjacents&nbsp;? La réponse se trouve dans l'analyse des
propriétés arithmétiques. Additionner deux nombres de <emphasis>k</emphasis>
bits renvoie un résultat large d'au plus <emphasis>k</emphasis>+1 bits.
Ainsi, un champ de <emphasis>k</emphasis>+1 bits peut recevoir en toute
sécurité le résultat d'une telle opération, même s'il est produit par
une instruction ordinaire.
</para>

<para>
Supposons donc que les champs de 8 bits de nos précédents exemples
soient désormais des champs de 7 bits avec <quote>espaces de séparation
pour retenue</quote> d'un bit chacun&nbsp;:
</para>

<para>

<programlisting>
              PE3          PE2          PE1          PE0
      +----+-------+----+-------+----+-------+----+-------+
Reg0  | D' | D 6:0 | C' | C 6:0 | B' | B 6:0 | A' | A 6:0 |
      +----+-------+----+-------+----+-------+----+-------+
</programlisting>

</para>

<para>
Mettre en place un vecteur d'additions 7 bits se fait de la manière
suivante&nbsp;: On part du principe qu'avant l'exécution de toute instruction
partitionnée, les bits des séparateurs de retenue
(<literal>A'</literal>, <literal>B'</literal>,
<literal>C'</literal>, et <literal>D'</literal>)
sont nuls. En effectuant simplement une addition ordinaire, tous les
champs reçoivent une valeur correcte sur 7 bits. En revanche, certains
bits de séparation peuvent passer à 1. On peut remédier à cela en
appliquant une seule opération supplémentaire conventionnelle et masquant
les bits de séparation. Notre vecteur d'additions entières sur 7 bits,
<literal>x</literal>+<literal>y</literal>, devient
ainsi&nbsp;:
</para>

<para>

<programlisting>
((x + y) &amp; 0x7f7f7f7f)
</programlisting>

</para>

<para>
Ceci nécessite seulement deux opérations pour effectuer quatre additions.
Le gain en vitesse est évident.
</para>

<para>
Les lecteurs avertis auront remarqué que forcer les bits de séparation
à zéro ne convient pas pour les soustractions. La solution est toutefois
remarquablement simple&nbsp;: Pour calculer <literal>x</literal>-<literal>y</literal>,
on garantira simplement la condition initiale, qui veut que tous les bits
de séparation de <literal>x</literal> valent 1 et que tous ceux
de <literal>y</literal> soient nuls. Dans le pire des cas, nous
obtiendrons&nbsp;:
</para>

<para>

<programlisting>
(((x | 0x80808080) - y) &amp; 0x7f7f7f7f)
</programlisting>

</para>

<para>
On peut toutefois souvent se passer du <quote>OU</quote> logique en s'assurant
que l'opération qui génère la valeur de <literal>x</literal>
utilise <literal>| 0x80808080</literal> plutôt que
<literal>&amp; 0x7f7f7f7f</literal> en dernière étape.
</para>

<para>
Laquelle de ces méthodes doit-on appliquer aux opérations partitionnées du
SWAR&nbsp;? La réponse est simple&nbsp;: <quote>celle qui offre le gain en rapidité le
plus important</quote>. Ce qui est intéressant, c'est que la méthode idéale peut
être différente selon chaque taille de champ, et ce à l'intérieur d'un
même programme, s'exécutant sur une même machine.
</para>

</sect4>

</sect3>

<sect3>
<title>Opérations de communication et de conversion de type</title>

<para>
Bien que certains calculs en parallèle, incluant les opérations sur les
pixels d'une image, ont pour propriété le fait que la <emphasis>n</emphasis>ième
valeur d'un vecteur soit une fonction des valeurs se trouvant à la <emphasis>n</emphasis>ième
position des vecteurs des opérandes, ce n'est généralement pas le cas. Par exemple,
même les opérations sur les pixels telles que l'adoucissement ou le flou réclament
en opérande les valeurs des pixels adjacents, et les transformations comme la transformée
de Fourrier (FFT) nécessitent des schémas de communications plus complexes (et moins
localisés).
</para>

<para>
Il est relativement facile de mettre efficacement en place un système de communication
à une dimension entre les valeurs du voisinage immédiat pour effectuer du SWAR, en utilisant
des opérations de décalage non partitionnées. Par exemple, pour déplacer une valeur depuis
<literal>PE</literal> vers <literal>PE</literal>(<emphasis>n</emphasis>+1),
un simple décalage logique suffit. Si les champs sont larges de 8 bits, on utilisera&nbsp;:
</para>

<para>

<programlisting>
(x &#60;&#60; 8)
</programlisting>

</para>

<para>
Pourtant, ce n'est pas toujours aussi simple. Par exemple, pour déplacer une valeur
depuis <literal>PE</literal><emphasis>n</emphasis> vers
<literal>PE</literal>(<emphasis>n</emphasis>-1), un simple décalage vers la
droite devrait suffire&hellip; mais le langage C ne précise pas si le décalage vers la
droite conserve le bit de signe, et certaines machines ne proposent, vers la droite,
qu'un décalage signé. Aussi, d'une manière générale, devons-nous mettre explicitement à
zéro les bits de signe pouvant être répliqués.
</para>

<para>

<programlisting>
((x &#62;&#62; 8) &amp; 0x00ffffff)
</programlisting>

</para>

<para>

L'ajout de connexions <quote>à enroulement</quote> 
(<quote><foreignphrase>wrap-around</foreignphrase></quote>) à l'aide de 
décalages non partitionnés <footnote><para>

N.D.T.&nbsp;: il s'agit en fait de simuler la ré-entrée par le coté 
opposé des données éjectées par le décalage. L'exemple qui suit permet 
d'implémenter ce cas en langage C, mais la plupart des microprocesseurs 
sont équipés d'instructions de rotation effectuant cela en une seule 
opération.

</para></footnote> est aussi raisonnablement efficace. Par exemple, pour 
déplacer une valeur depuis <literal>PE</literal><emphasis>i</emphasis> 
vers <literal>PE</literal>(<emphasis>i</emphasis>+1) avec 
enroulement&nbsp;:

</para>

<programlisting>
((x &#60;&#60; 8) | ((x &#62;&#62; 24) &amp; 0x000000ff))
</programlisting>

<para>

Les problèmes sérieux apparaissent lorsque des schémas de communications 
plus généraux doivent être mis en &oelig;uvre. Seul le jeu 
d'instructions du MAX de HP permet le réarrangement arbitraire des 
champs en une seule instruction, nommée <literal>Permute</literal>. 
Cette instruction <literal>Permute</literal> porte vraiment mal son 
nom&nbsp;: Non seulement elle effectue une permutation arbitraire des 
champs, mais elle autorise également les répétitions. En bref, elle met 
en place une opération arbitraire de <literal>x[y]</literal>.

</para>

<para>

Il reste malheureusement très difficile d'implémenter 
<literal>x[y]</literal> sans le concours d'une telle instruction. La 
séquence de code est généralement à la fois longue et inefficace, parce 
qu'en fait, il s'agit d'un programme séquentiel. Ceci est très décevant. 
La vitesse relativement élevée des opérations de type 
<literal>x[y]</literal> sur les les supercalculateurs SIMD MasPar 
MP1/MP2 et Thinking Machines CM1/CM2/CM200 était une des clés majeures 
de leur succès. Toutefois, un <literal>x[y]</literal> reste plus lent 
qu'une communication de proximité, même sur ces supercalculateurs. 
Beaucoup d'algorithmes ont donc été conçus pour réduire ces besoins en 
opérations de type <literal>x[y]</literal>. Pour simplifier, disons que 
sans soutien matériel, le meilleur est encore très certainement de 
développer des algorithmes SWAR en considérant <literal>x[y]</literal> 
comme étant interdit, ou au moins très coûteux.

</para>

</sect3>

<sect3>
<title>Opérations récurrentes (réductions, balayages, et cætera)</title>

<para>
Une récurrence est un calcul dans lequel il existe une relation
séquentielle apparente entre les valeurs à traiter. Si toutefois des
opérations associatives sont impliquées dans ces récurrences, il peut
être possible de recoder ces calculs en utilisant un algorithme
parallèle à structure organisée en arbre.
</para>

<para>
Le type de récurrence parallélisable le plus courant est probablement
la classe connue sous le nom de réduction associative. Par exemple,
pour calculer la somme des valeurs d'un vecteur, on écrit du code C
purement séquentiel, comme&nbsp;:
</para>

<para>

<programlisting>
t = 0;
for (i=0; i&#60;MAX; ++i) t += x[i];
</programlisting>

</para>

<para>

Cependant, l'ordre des additions est rarement important. Les opérations
en virgule flottante et les saturations peuvent rendre différents résultats
si l'ordre des additions est modifié, mais les additions ordinaires sur les
entiers (et qui reviennent au début après avoir atteint la valeur maximum)
renverront exactement les mêmes résultats, indépendemment de l'ordre dans
lequel elles sont effectuées. Nous pouvons ainsi réécrire cette séquence
sous la forme d'une somme parallèle structurée en arbre, dans laquelle nous
additionnons d'abord des paires de valeurs, puis des paires de ces sous-totaux,
et ainsi de suite jusqu'à l'unique somme finale. Pour un vecteur de quatre
valeurs 8 bits, seulement deux additions sont nécessaires. La première effectue
deux additions de 8 bits et retourne en résultat deux champs de 16 bits,
contenant chacun un résultat sur 9 bits&nbsp;:

</para>

<para>

<programlisting>
t = ((x &amp; 0x00ff00ff) + ((x &#62;&#62; 8) &amp; 0x00ff00ff));
</programlisting>

</para>

<para>
La deuxième fait la somme de ces deux valeurs de 9 bits dans
un champs de 16 bits, et renvoie un résultat sur 10 bits&nbsp;:
</para>

<para>

<programlisting>
((t + (t &#62;&#62; 16)) &amp; 0x000003ff)
</programlisting>

</para>

<para>

En réalité, la seconde opération effectue l'addition de deux champs de 
16 bits&hellip; mais les 16 bits de poids fort n'ont pas de 
signification. C'est pourquoi le résultat est masqué en une valeur de 10 
bits.

</para>

<para>

Les balayages (<quote><foreignphrase>scans</foreignphrase></quote>), 
aussi connus sous le nom d'opérations <quote>à préfixe parallèle</quote> 
sont un peu plus difficile à mettre en &oelig;uvre efficacement. Ceci 
est dû au fait que contrairement aux réductions, les balayages peuvent 
produire des résultats partitionnées.

</para>

</sect3>

</sect2>

<sect2>
<title>SWAR MMX sous Linux</title>

<para>

Pour Linux, nous nous soucierons principalement des processeurs IA32. La 
bonne nouvelle, c'est qu'AMD, Cyrix et Intel implémentent tous le même 
jeu d'instructions MMX. En revanche, les performances de ces différents 
MMX sont variables. Le K6, par exemple, n'est doté que d'un seul 
<foreignphrase>pipeline</foreignphrase> là où le Pentium MMX en a deux. 
La seule nouvelle vraiment mauvaise, c'est qu'Intel diffuse toujours ces 
stupides films publicitaires sur le MMX&hellip; ;-)

</para>

<para>

Il existe trois approches sérieuses du SWAR par le MMX&nbsp;:

</para>

<orderedlist>

<listitem><para>

L'utilisation des fonctions d'une bibliothèque dédiée au MMX. Intel, en 
particulier, a développé plusieurs <quote>bibliothèques de 
performances</quote>, offrant toute une gamme de fonctions optimisées à 
la main et destinées aux tâches multimédia courantes. Avec un petit 
effort, bon nombre d'algorithmes non-multimédia peuvent être 
retravaillés pour permettre à quelques unes des zones de calcul intensif 
de s'appuyer sur une ou plusieurs de ces bibliothèques. Ces 
bibliothèques ne sont pas disponibles sous Linux, mais pourraient être 
adaptées pour le devenir.

</para></listitem>

<listitem><para>

L'utilisation directe des instructions MMX. C'est assez compliqué, pour
deux raisons&nbsp;: D'abord, le MMX peut ne pas être disponible sur le processeur
concerné, ce qui oblige à fournir une alternative. Ensuite, l'assembleur IA32
utilisé en général sous Linux ne reconnaît actuellement pas les instructions
MMX.

</para></listitem>

<listitem><para>

L'utilisation d'un langage de haut niveau ou d'un module du 
compilateur qui puisse directement générer des instructions MMX 
appropriées. Quelques outils de ce type sont actuellement en cours de 
développement, mais aucun n'est encore pleinement fonctionnel sous 
Linux. Par exemple, à l'Université de Purdue (<ulink 
url="&page-auteur;SWAR/"/>), nous développons 
actuellement un compilateur qui admettra des fonctions écrites dans un 
<quote>dialecte</quote> explicite parallèle au langage C et qui générera 
des modules SWAR accessibles comme des fonctions C ordinaires, et qui 
feront usage de tous les supports SWAR disponibles, y compris le MMX. 
Les premiers prototypes de compilateurs à modules ont été générés à 
Fall, en 1996. Amener cette technologie vers un état réellement 
utilisable prend cependant beaucoup plus de temps que prévu 
initialement.

</para></listitem>

</orderedlist>

<para>
En résumé, le SWAR par MMX est toujours d'un usage malaisé. Toutefois, avec
quelques efforts supplémentaires, la seconde approche décrite ci-dessus peut
être utilisée dès à présent. En voici les bases&nbsp;:
</para>

<para>

<orderedlist>
<listitem>

<para>
Vous ne pourrez pas utiliser le MMX si votre processeur ne le prend pas
en charge. Le code GCC suivant vous permettra de savoir si votre processeur
est équipé de l'extension MMX. Si c'est le cas, la valeur renvoyée sera
différente de zéro, sinon nulle.

<programlisting>
inline extern
int mmx_init(void)
{
        int mmx_disponible;

        __asm__ __volatile__ (
                /* Récupère la version du CPU */
                "movl $1, %%eax\n\t"
                "cpuid\n\t"
                "andl $0x800000, %%edx\n\t"
                "movl %%edx, %0"
                : "=q" (mmx_disponible)
                : /* pas d'entrée */
        );
        return mmx_disponible;
}
</programlisting>

</para>
</listitem>
<listitem>

<para>
Un registre MMX contient essentiellement ce que GCC appellerait un
<literal>unsigned long long</literal>. Ainsi, ce sont des variables
de ce type et résidant en mémoire qui vont former le mécanisme de communication
entre les modules MMX et le programme C qui les appelle. Vous pouvez aussi déclarer
vos données MMX comme étant des structures de données alignées sur des adresses
multiples de 64 bits (il convient de garantir l'alignement sur 64 bits en déclarant
votre type de données comme étant membre d'une <literal>union</literal>
comportant un champ <literal>unsigned long long</literal>).
</para>
</listitem>
<listitem>

<para>
Si le MMX est disponible, vous pouvez écrire votre code MMX en utilisant
la directive assemble <literal>.byte</literal> pour coder chaque
instruction. C'est un travail pénible s'il est abattu à la main, mais pour un
compilateur, les générer n'est pas très difficile. Par exemple, l'instruction MMX
<literal>PADDB MM0,MM1</literal> pourrait être encodée par la ligne
d'assembleur in-line GCC suivante&nbsp;:


<programlisting>
__asm__ __volatile__ (".byte 0x0f, 0xfc, 0xc1\n\t");
</programlisting>


Souvenez-vous que le MMX utilise une partie du matériel destiné aux opérations
en virgule flottante, et donc que le code ordinaire mélangé au MMX ne doit pas
invoquer ces dernières. La pile en virgule flottante doit également être vide
avant l'exécution de tout code MMX. Cette pile est normalement vide à l'entrée
d'une fonction C ne faisant pas usage de la virgule flottante.


</para>
</listitem>
<listitem>

<para>
Clôturez votre code MMX par l'exécution de l'instruction <literal>EMMS</literal>,
qui peut être encodée par&nbsp;:


<programlisting>
__asm__ __volatile__ (".byte 0x0f, 0x77\n\t");
</programlisting>

</para>
</listitem>

</orderedlist>

</para>

<para>
Tout ce qui précède paraît rébarbatif, et l'est. Ceci dit, le MMX
est encore assez jeune&hellip; de futures versions de ce document proposeront
de meilleures techniques pour programmer le SWAR MMX.
</para>

</sect2>

</sect1>

<sect1>
<title>Processeurs auxiliaires des machines Linux</title>

<para>
Même si cette approche n'est plus à la mode depuis quelques temps,
il reste virtuellement impossible aux autres méthodes de maintenir à
la fois prix bas et performances élevées, chose habituellement rendue
possible par l'utilisation d'un système Linux, lorsqu'il s'agit
d'héberger dans la machine une unité de calcul dédiée. Le problème est
que la prise en charge logicielle de ces outils est très limitée.
Nous serons plus ou moins livrés à nous mêmes.
</para>

<sect2>
<title>Un PC Linux est une bonne station d'accueil</title>

<para>
En général, les processeurs secondaires tendent à se spécialiser dans
l'exécution de fonctions bien spécifiques.
</para>

<para>
Avant de se sentir découragés parce que livrés à nous-mêmes, il est
utile de comprendre que, bien qu'il puisse être difficile de le préparer
à recevoir un système particulier, un PC sous Linux reste l'une des rares
plate-formes qui se prêtent convenablement à cet usage.
</para>

<para>
Un PC forme un bon système d'accueil pour deux raisons&nbsp;: La première est sa capacité
à évoluer aisément et de façon peu onéreuse. Les ressources telles que
la mémoire, les disques, le réseau et autres peuvent être ajoutées
vraiment très facilement à un PC. La seconde est sa facilité d'interfaçage.
Non seulement les prototypes de cartes ISA et PCI sont largement répandus
et disponibles, mais le port parallèle fournit également des performances
raisonnables dans une interface discrète. L'adressage séparé des entrées&nbsp;/&nbsp;sorties
de l'architecture IA32 facilite également l'interfaçage en offrant
une protection de ces adresses au niveau du port individuel.
</para>

<para>
Linux est également un bon système d'exploitation pour l'hébergement de
systèmes dédiés. La libre et complète disponibilité du code source, et les
nombreux manuels de programmation avancée constituent évidement une aide
très précieuse. Mais Linux apporte également une gestion des tâches pratiquement
temps-réel. Il existe même une version fonctionnant en vrai temps-réel.
Mais une chose peut-être plus importante encore est le fait que, même
dans un environnement Unix complet, Linux sait prendre en charge des outils
de développement écrits pour fonctionner sous MS-DOS ou Windows. Les programmes
DOS peuvent être exécutés dans un processus Unix en utilisant
<literal>dosemu</literal>, qui dresse une machine virtuelle protégée
qui peut littéralement exécuter du code MS-DOS. La prise en charge des programmes
Windows 3.xx sous Linux est encore plus directe&nbsp;: certains 
logiciels libres comme
<ulink url="http://www.winehq.org"><literal>wine</literal></ulink>
simulent Windows 3.11<footnote><para>

N.D.T.&nbsp;: WINE a évolué avec le temps et reconnaît naturellement
les versions plus récentes de ce système d'exploitation.

</para></footnote>

suffisamment bien pour permettre à la plupart des programmes
Windows d'être exécutés correctement sur une machine Unix équipée de 
X-Window.

</para>

<para>
Les deux sections suivantes donnent des exemples de systèmes parallèles
auxiliaires que j'aimerais voir être exploités sous Linux&nbsp;:
</para>

</sect2>

<sect2>
<title>Avez-vous essayé le DSP&nbsp;?</title>

<para>
Un marché prospère s'est développé autour des puces DSP (<quote><foreignphrase>Digital
Signal Processing</foreignphrase></quote>, ou <quote>Traitement Numérique du Signal</quote>) à hautes performances. Bien
qu'elles soient en général conçues pour être embarquées dans des systèmes dédiés à
des usages bien spécifiques, elles peuvent aussi être utilisées comme de très bons
ordinateurs parallèles annexes. Voici pourquoi&nbsp;:
</para>

<para>

<itemizedlist>
<listitem>

<para>
Plusieurs d'entre elles, comme le TMS320 de
<ulink url="http://www.ti.com">Texas Instruments</ulink> et la famille
SHARC d'<ulink url="http://www.analog.com">Analog Devices</ulink> sont
conçues pour permettre l'assemblage de machines parallèles
en n'utilisant que très peu, voire aucun circuit logique intermédiaire.
</para>
</listitem>
<listitem>

<para>
Ces puces sont bon marché, spécialement au niveau du coût par MIP ou par
MFLOP. Prix des circuits de gestion logique de base compris, on peut trouver
un processeur DSP pour le dixième du prix du processeur d'un PC, à performances
comparables.

</para>
</listitem>
<listitem>

<para>
Elle ne nécessitent que peu de puissance et ne dissipent pas beaucoup de
chaleur. Cela signifie qu'il est possible d'alimenter toute une poignée de processeurs
de ce type avec une simple alimentation PC conventionnelle, et de les enfermer
dans le boîtier d'un PC sans transformer celui-ci en haut-fourneau.

</para>
</listitem>
<listitem>

<para>
La plupart des jeux d'instructions des DSP contiennent des choses assez
exotiques et que les langages de haut niveau (c'est-à-dire&nbsp;: comme le
C) ne sont pas à même d'utiliser correctement. Par exemple, l'<quote>adressage de
bit inversé</quote>. En utilisant des systèmes parallèles inclus dans une machine,
il est possible de compiler et d'exécuter directement la plupart du code sur
cette machine, tout en confiant l'exécution des quelques algorithmes consommant
la plupart du temps système aux DSP, par l'entremise d'un code particulièrement soigné
et optimisé à la main.

</para>
</listitem>
<listitem>

<para>
Ces DSP ne sont pas vraiment conçus pour faire fonctionner un système d'exploitation
UNIX ou assimilé, et ne sont généralement pas non plus adaptés à une utilisation
autonome comme processeur général d'un ordinateur. Le système de gestion de la mémoire,
par exemple, est insuffisant pour beaucoup d'entre eux. En d'autres mots, ils sont bien
plus efficaces lorsqu'ils sont intégrés au sein d'une machine hôte plus polyvalente
&hellip; telle qu'un PC sous Linux.
</para>
</listitem>

</itemizedlist>

</para>

<para>
Bien que de nombreux modems et cartes son contiennent des processeurs DSP
accessibles par des pilotes Linux, les grands profits arrivent avec l'utilisation de
systèmes parallèles intégrés comprenant au moins quatre processeurs DSP.
</para>

<para>
Même si la série TMS320 de Texas Instruments, sur <ulink
url="http://dspvillage.ti.com">http://dspvillage.ti.com</ulink
>, est populaire depuis un bon moment, les systèmes de ce type étant
disponibles sont encore peu nombreux. Il existe à la fois une version
uniquement entière et une version virgule flottante du TMS320. Les
anciens modèles utilisaient un format de virgule flottante en simple précision assez
inhabituel, mais les nouveaux gèrent à présent les formats IEEE. Les anciens TMS320C4x
(ou simplement <quote>C4x</quote>), accomplissaient jusqu'à 80 MFLOP en utilisant le format de
nombre en virgule flottante simple précision spécifique à TI. En comparaison, un seul
<quote>C67x</quote> apportera jusqu'à 1 GFLOP en simple précision et 420 MFLOP en double précision
au format IEEE, en utilisant une architecture à base de VLIW nommée VelociTI. Non seulement
il est aisé de configurer un groupe de circuits de ce type pour en faire un multiprocesseur,
mais un de ces circuits, le multiprocesseur <quote>'C8x</quote>, formera à lui seul un processeur
principal à technologie RISC accusant 100 MFLOP au format IEEE, et doté soit de deux, soit
de quatre DSP esclaves intégrés.
</para>

<para>
L'autre famille de processeurs DSP qui ne soit pas simplement utilisée
ces temps-ci par quelques rares systèmes en parallèle est SHARC (ou
<quote>ADSP-2106x</quote>), d'<ulink url="http://www.analog.com/"
>Analog Devices</ulink>.
Ces circuits peuvent être configurés en un multiprocesseur à mémoire partagée
formé par 6 processeurs sans nécessiter de logique externe pour les associer, et
peuvent aussi former de plus grands systèmes grâce à des circuits de liaison de
4 bits. Les systèmes au-delà de celui-ci sont essentiellement destinés aux applications
militaires, et reviennent assez cher. Toutefois, 
<ulink url="http://www.iced.com/">Integrated Computing Engines, Inc.</ulink
> produit un petit jeu de deux cartes PCI assez intéressant, nommé GreenICE. Cette
unité contient un réseau de 16 processeurs SHARC, et affiche une vitesse de pointe
d'1.9 GFLOP au format IEEE simple précision. GreenICE coûte moins de 5000 dollars.
</para>

<para>
À mon avis, les circuits DSP parallèles intégrés mériteraient une plus grande attention
de la part du petit monde du calcul en parallèle sous Linux&hellip;
</para>

</sect2>

<sect2>
<title>Calcul à l'aide des FPGA et circuits logiques reconfigurables</title>

<para>

Si l'objectif final du traitement en parallèle reste l'atteinte des plus 
hautes vitesses possibles, et bien pourquoi ne pas fabriquer du matériel 
sur mesure&nbsp;? Nous connaissons tous la réponse&nbsp;: Trop cher, 
trop long à développer, le matériel ainsi conçu devient inutile lorsque 
l'algorithme change même de façon minime, et cætera. Cependant, les 
récentes avancées faites dans le domaine des FPGA 
(<quote><foreignphrase>Field Programmable Gate 
Array</foreignphrase></quote>, ou <quote>Réseaux Logiques Programmables 
à effet de Champ</quote>) ont réduit à néant la plupart de ces 
objections. Aujourd'hui, la densité des portes logiques est suffisamment 
élevée pour qu'un processeur entier puisse être intégré dans un seul 
FPGA, et le temps de reconfiguration (ou de reprogrammation) d'un de ces 
FPGA a également chuté à un niveau où l'on peut raisonnablement 
reconfigurer le circuit même entre deux phases d'un même algorithme.

</para>

<para>
Il ne faut pas avoir froid aux yeux pour utiliser ces techniques&nbsp;: Il
faudra travailler avec des langages de description matérielle tels que
le VHDL pour configurer les FPGA, tout comme écrire du code de bas niveau
pour programmer les interfaces avec le système d'accueil Linux. En revanche,
le coût des FPGA est assez bas, spécialement pour les algorithmes opérant
sur des données entières et de précision normale (ce qui ne représente en fait
qu'un surensemble restreint de tout ce à quoi le SWAR s'applique bien), ces FPGA
peuvent effectuer des opérations complexes à peu près aussi vite qu'on peut les
leur transmettre. Par exemple, de simples systèmes à base de FPGA ont accusé des
performances supérieures à celles des supercalculateurs lors de recherches sur
des bases de données génétiques.
</para>

<para>
Il existe plusieurs compagnies produisant du matériel approprié à base de FPGA,
mais les deux suivantes en sont un bon exemple&nbsp;:
</para>

<para>

Virtual Computer Company propose toute une gamme de produits utilisant 
des FPGA Xilinx à base de SRAM et reconfigurables dynamiquement. Leur 
<quote>Virtual ISA Proto Board</quote> 8/16 bits coûte moins de 2000 
dollars.

</para>

<para>
L'ARC-PCI d'Altera (<quote><foreignphrase>Altera Reconfigurable Computer</foreignphrase></quote>, sur bus PCI), sur <ulink
url="http://www.altera.com/html/new/pressrel/pr_arc-pci.html"
>http://www.altera.com/html/new/pressrel/pr_arc-pci.html</ulink
>, est un type de carte similaire, mais utilisant les FPGA d'Altera et un
bus PCI comme interface plutôt qu'un bus ISA.
</para>

<para>

Bon nombre de ces outils de conception, langages de description 
matérielle, compilateurs, routeurs, et cætera, sont livrés sous forme 
d'un code objet qui ne fonctionne que sous DOS ou Windows. Nous 
pourrions simplement conserver une partition DOS/Windows sur notre PC 
d'accueil et redémarrer lorsque nous en avons besoin. Toutefois, la 
plupart de ces outils peuvent probablement fonctionner sous Linux en 
utilisant <literal>dosemu</literal> ou des émulateurs Windows tels que 
<literal>wine</literal>.

</para>

</sect2>

</sect1>

<sect1>
<title>D'intérêt général</title>

<para>
Les sujets couverts dans cette section s'appliquent aux quatre
modèles de traitement en parallèle sous Linux.
</para>

<sect2>
<title>Compilateurs et langages de programmation</title>

<para>
Je suis principalement un <quote>chercheur en compilateurs</quote>. Je devrais
donc être capable de dire s'il y a beaucoup de compilateurs vraiment
performants, générant du code parallèle efficace pour les systèmes Linux.
Malheureusement, et pour dire la vérité, il est difficile de battre les performances
obtenues en exprimant votre programme en parallèle avec des communications
et autres opérations parallèles, le tout dans un code en langage C, compilé
par GCC.
</para>

<para>
Les projets de compilateurs ou de langages suivants représentent une
partie des meilleurs efforts produits pour la génération d'un code
raisonnablement efficace depuis les langages de haut niveau. Généralement,
chacun d'eux est raisonnablement efficace pour les tâches qu'il vise,
mais aucun ne correspond aux langages et aux compilateurs puissants et
tout-terrain qui vous feront abandonner définitivement l'écriture de programmes
C compilés par GCC&hellip; ce qui est très bien comme çà. Utilisez ces langages
et compilateurs à ce pour quoi ils ont été conçus, et vous serez récompensés
par des durées de développement plus courtes, des opérations de maintenance et de
débogages réduites, et cætera.
</para>

<para>
Il existe un grand nombre de langages et de compilateurs en dehors de ceux listés
ici (dans l'ordre alphabétique). Une liste de compilateurs librement
disponibles (la plupart n'ayant rien à voir avec le traitement en parallèle
sous Linux) est accessible ici&nbsp;: <ulink
url="http://www.idiom.com/free-compilers"
>http://www.idiom.com/free-compilers</ulink
>.
</para>

<sect3>
<title>Fortran 66/77/PCF/90/HPF/95</title>

<para>
Au moins dans la communauté de l'informatique scientifique, le Fortran
sera toujours présent. Bien sûr, le Fortran d'aujourd'hui n'a plus la
même signification que celle définie par le standard ANSI de 1966. En
quelques mots, le Fortran 66 était très limité. Le Fortran 77 a ajouté
des nuances dans ses fonctions, la plus intéressante étant la prise en
charge améliorée des données de type caractère et la modification de la
sémantique des boucles <literal>DO</literal>. Le Fortran PCF
(<quote><foreignphrase>Parallel Computing Forum</foreignphrase></quote>) a tenté d'ajouter diverses fonctions de
gestion de traitement en parallèle au Fortran 77. Le Fortran 90 est un langage moderne
et pleinement fonctionnel, qui apporte essentiellement des facilités de programmation
orientée objet ressemblant au C++ et une syntaxe de tableaux en parallèle au langage
du Fortran 77. HPF (<quote><foreignphrase>High-Performance Fortran</foreignphrase></quote>, lui-même
proposé en deux versions (HPF-1 et HPF-2), est essentiellement
la version standardisée et améliorée de ce que beaucoup d'entre nous ont connu
sous les noms de CM Fortran, MasPar Fortran, ou Fortran D. Il étend le Fortran 90
avec diverses améliorations dédiées au traitement en parallèle, très centrées
sur la spécification d'agencements de données. Citons enfin le Fortran 95,
version quelque peu améliorée et raffinée du Fortran 90.
</para>

<para>
Ce qui fonctionne avec le C peut en général fonctionner aussi avec  <literal>f2c</literal>,
<literal>g77</literal>, ou les produits Fortran 90/95 commerciaux de NAG.
Ceci est dû au fait que tous ces compilateurs peuvent au final produire le
même code que celui utilisé en arrière-boutique par GCC.
</para>

<para>
Les paralléliseurs Fortran commerciaux pouvant générer du code pour SMP
sont disponibles sur <ulink
url="http://www.kai.com"
>http://www.kai.com</ulink
> et <ulink url="http://www.crescentbaysoftware.com">http://www.crescentbaysoftware.com</ulink>.
Il n'est pas clairement indiqué si ces compilateurs sont utilisables pour
des machines SMP Linux, mais ils devraient l'être étant donné que les <foreignphrase>threads</foreignphrase>
POSIX standards (soit les LinuxThreads) fonctionnent sur des systèmes SMP sous Linux.
</para>

<para>
Le <ulink
url="http://www.pgroup.com/"
>Portlan Group</ulink
> propose des compilateurs Fortran HPF (et C/C++) parallèles commerciaux
générant du code Linux SMP. Il existe aussi une version ciblant les <foreignphrase>clusters</foreignphrase>
s'appuyant sur MPI ou PVM. Les produits de <ulink
url="http://www.apri.com"
>http://www.apri.com</ulink
> pourraient aussi être utiles sur des <foreignphrase>clusters</foreignphrase> ou des machines SMP.
</para>

<para>
Parmi les Fortran parallélisés disponibles librement et qui pourraient
fonctionner sur des systèmes Linux en parallèle, citons&nbsp;:
</para>

<itemizedlist>
<listitem>

<para>
ADAPTOR (<quote><foreignphrase>Automatic DAta Parallelism TranslaTOR</foreignphrase></quote>, <ulink
url="http://www.gmd.de/SCAI/lab/adaptor/adaptor_home.html"
>http://www.gmd.de/SCAI/lab/adaptor/adaptor_home.html</ulink>,
qui peut traduire du Fortran HPF en Fortran 77/90 avec appels MPI
ou PVM, mais qui ne mentionne pas Linux.
</para>
</listitem>
<listitem>

<para>
Fx, de l'Université Carnegie Mellon (N.D.T.&nbsp;: Pittsburgh), vise 
certains
<foreignphrase>clusters</foreignphrase> de stations de travail, mais Linux&nbsp;?
</para>
</listitem>
<listitem>

<para>
HPFC (un prototype de Compilateur HPF) génère du code Fortran 77
avec appels PVM. Est-il utilisable sur un <foreignphrase>cluster</foreignphrase> Linux&nbsp;?
</para>
</listitem>
<listitem>

<para>
PARADIGM (<quote><foreignphrase>PARAllelizing compiler for DIstributed-memory
General-purpose Multicomputers</foreignphrase></quote>, <ulink
url="http://www.crhc.uiuc.edu/Paradigm"
>http://www.crhc.uiuc.edu/Paradigm</ulink>) peut-il
lui aussi être utilisé avec Linux&nbsp;?
</para>
</listitem>
<listitem>

<para>
Le compilateur Polaris génère du code Fortran pour multiprocesseurs à mémoire partagée,
et pourrait bientôt être reciblé vers les <foreignphrase>clusters</foreignphrase> Linux PAPERS.

</para>
</listitem>
<listitem>

<para>
PREPARE, <ulink
url="http://www.irisa.fr/EXTERNE/projet/pampa/PREPARE/prepare.html"
>http://www.irisa.fr/EXTERNE/projet/pampa/PREPARE/prepare.html</ulink>,
vise les <foreignphrase>clusters</foreignphrase> MPI&hellip; On ne sait pas
vraiment s'il sait générer du code pour processeurs IA32.

</para>
</listitem>
<listitem>

<para>
Combinant ADAPT et ADLIB, <quote>shpf</quote> (<quote><foreignphrase>Subset High Performance Fortran
compilation system</foreignphrase></quote>)
est dans le domaine public et génère du code Fortran 90 avec appels MPI.
Donc, si vous avez un compilateur Fortran 90 sous Linux&hellip;

</para>
</listitem>
<listitem>

<para>
SUIF (<quote><foreignphrase>Stanford University Intermediate Form</foreignphrase></quote>, voir <ulink
url="http://suif.stanford.edu">
http://suif.stanford.edu</ulink
>) propose des compilateurs parallélisés pour le C et le Fortran.
C'est aussi l'objectif du <foreignphrase>National Compiler Infrastructure Project</foreignphrase>&hellip;
<quote>Quelqu'un s'occupe-t-il des systèmes parallèles sous Linux&nbsp;?</quote>
</para>
</listitem>

</itemizedlist>

<para>

Je suis sûr d'avoir omis plusieurs compilateurs potentiellement utiles 
dans les différents dialectes du Fortran, mais ils sont si nombreux 
qu'il est difficile d'en garder la trace. A l'avenir, je préférerais ne 
lister que ceux qui sont réputés fonctionner sous Linux. Merci de 
m'envoyer commentaires et corrections en anglais par courrier 
électronique à

<email>&addr-auteur;</email>.

</para>

</sect3>

<sect3>
<title>GLU (<quote><foreignphrase>Granular Lucid</foreignphrase></quote>)</title>

<para>
GLU (<quote><foreignphrase>Granular Lucid</foreignphrase></quote>) est un système de programmation de très haut niveau
basé sur un modèle hybride combinant les modèles intentionnel (<quote><foreignphrase>Lucid</foreignphrase></quote>) et
impératif. Il reconnaît à la fois les <foreignphrase>sockets</foreignphrase> PVM et TCP. Fonctionne-t-il sous
Linux&nbsp;? (Le site du <quote><foreignphrase>Computer Science Laboratory</foreignphrase></quote> se trouve sur
<ulink url="http://www.csl.sri.com"
>http://www.csl.sri.com</ulink>.
</para>

</sect3>

<sect3>
<title>Jade et SAM</title>

<para>
Jade est un langage de programmation en parallèle sous forme d'extension au
langage C et exploitant les concurrences de la granularité des programmes séquentiels et
impératifs. Il s'appuie sur un modèle de mémoire partagée distribuée, modèle implémenté
par SAM dans les <foreignphrase>clusters</foreignphrase> de stations de travail utilisant PVM. Plus d'informations
sur
<ulink
url="http://suif.stanford.edu/~scales/sam.html"
>http://suif.stanford.edu/~scales/sam.html</ulink>.
</para>

</sect3>

<sect3>
<title>Mentat et Legion</title>

<para>
Mentat est un système de traitement en parallèle orienté objet qui fonctionne
avec des <foreignphrase>clusters</foreignphrase> de stations de travail et qui a été porté vers Linux. Le
<quote><foreignphrase>Mentat Programming Language</foreignphrase></quote> (MPL) est un langage de programmation orienté
objet basé sur le C++. Le système temps-réel de Mentat utilise quelque chose
qui ressemble vaguement à des appels de procédures à distance (<quote><foreignphrase>remote
procédure calls</foreignphrase></quote>&nbsp;: RPC) non bloquantes. Plus d'informations sur <ulink
url="http://www.cs.virginia.edu/~mentat"
>http://www.cs.virginia.edu/~mentat</ulink
>.
</para>

<para>
Legion <ulink
url="http://www.cs.virginia.edu/~legion/"
>http://www.cs.virginia.edu/~legion</ulink
> est construit au dessus de Mentat, et simule une machine virtuelle unique
au travers des machines d'un réseau large zone.
</para>

</sect3>

<sect3>
<title>MPL (<quote><foreignphrase>MasPar Programming Language</foreignphrase></quote>)</title>

<para>

À ne pas confondre avec le MPL de Mentat, ce langage était initialement 
développé pour être le dialecte C parallèle natif des supercalculateurs 
SIMD MasPar. MasPar ne se situe plus vraiment sur ce segment (ils 
s'appellent maintenant <ulink url="http://www.neovista.com">NeoVista 
Solutions</ulink> et se spécialisent dans le <foreignphrase>data 
mining</foreignphrase><footnote><para>

N.D.T.&nbsp;: le <foreignphrase>data mining</foreignphrase> consiste à 
étudier et mettre au point des techniques efficaces de recherche d'une 
information au travers d'une immense quantité de données.

</para></footnote>, mais leur compilateur MPL a été construit en 
utilisant GCC. Il est donc librement disponible. Dans un effort concerté 
entre les université de l'Alabama, à Huntsville, et celle de Purdue, le 
MPL MasPar a été reciblé pour générer du code C avec appels AFAPI (voir 
la section 3.6), et fonctionne ainsi à la fois sur des machines Linux de 
type SMP et sur des <foreignphrase>clusters</foreignphrase>. Le 
compilateur est, néanmoins, quelque peu bogué&hellip; voir <ulink 
url="http://www.math.luc.edu/~laufer/mspls/papers/cohen.ps"/>.

</para>

</sect3>

<sect3>
<title>PAMS (<quote><foreignphrase>Parallel Application Management System</foreignphrase></quote>)</title>

<para>
Myrias est une compagnie qui vend un logiciel nommé PAMS (<quote><foreignphrase>Parallel
Application Management System</foreignphrase></quote>). PAMS fournit des directives très
simples destinées au traitement en parallèle utilisant de la mémoire
partagée virtuelle. Les réseaux de machines Linux ne sont pas encore
pris en charge.
</para>

</sect3>

<sect3>
<title>Parallaxis-III</title>

<para>
Parallaxis-III est un langage de programmation structurée qui reprend
Modula-2, en ajoutant des <quote>processeurs et connexions virtuels</quote> pour
le parallélisme des données (un modèle SIMD). La suite Parallaxis
comprend des compilateurs pour ordinateurs séquentiels et parallèles,
un débogueur (extension des débogueurs <literal>gdb</literal>
et <literal>xgdb</literal>), et une vaste gamme d'algorithmes
d'exemple de différents domaines, en particulier dans le traitement des
images. Il fonctionne sur les systèmes Linux séquentiels. Une ancienne
version prenait déjà en charge diverses plate-formes parallèles, et une
prochaine version le fera à nouveau (comprendre&nbsp;: ciblera les <foreignphrase>clusters</foreignphrase> PVM).
Plus d'informations sur <ulink
url="http://www.informatik.uni-stuttgart.de/ipvr/bv/p3/p3.html"
>http://www.informatik.uni-stuttgart.de/ipvr/bv/p3/p3.html</ulink>.
</para>

</sect3>

<sect3>
<title>pC++/Sage++</title>

<para>
pC++/Sage++ est une extension au langage C autorisant les opérations de
type <quote>données en parallèle</quote>, en utilisant des <quote>collections d'objets</quote>
formées à partir de classes <quote>éléments</quote> de base. Il s'agit d'un préprocesseur
générant du code C++ pouvant fonctionner en environnement PVM. Est-ce que
cela fonctionne sous Linux&nbsp;? Plus d'informations ici&nbsp;: <ulink
url="http://www.extreme.indiana.edu/sage/"
>http://www.extreme.indiana.edu/sage</ulink>.
</para>

</sect3>

<sect3>
<title>SR (<quote><foreignphrase>Synchronizing Resources</foreignphrase></quote>)</title>

<para>
SR (<quote><foreignphrase>Synchronizing Resources</foreignphrase></quote>, ou <quote>ressources synchronisées</quote>)
est un langage de programmation de situations concurrentes et dans lequel les ressources
encapsulent les processus et les variables qu'ils partagent. Les <quote>opérations</quote>
forment le principal mécanisme d'interactions entre processus. SR propose une
intégration novatrice des mécanismes utilisées pour invoquer et traiter les
opérations. En conséquence, les appels de procédures locaux ou à distance,
les rendez-vous, le <foreignphrase>message passing</foreignphrase>, la création dynamique de processus, la
multi-diffusion et les sémaphores sont tous pris en charge. SR gère également
les variables et opérations globales partagées.
</para>

<para>
Il existe une adaptation Linux, mais le type de parallélisme que SR peut y
faire fonctionner n'est pas clairement défini. Plus d'informations sur <ulink
url="http://www.cs.arizona.edu/sr/www/index.html"
>http://www.cs.arizona.edu/sr/www/index.html</ulink>.
</para>

</sect3>

<sect3>
<title>ZPL et IronMan</title>

<para>
ZPL est un langage de programmation par matrices conçu pour gérer les
applications scientifiques et d'ingénierie. Il génère des appels à une
interface de <foreignphrase>message-passing</foreignphrase> assez simple nommée IronMan, et les quelques
fonctions qui constituent cette interface peuvent facilement être
implémentées à l'aide de pratiquement n'importe quel système de <foreignphrase>message-passing</foreignphrase>.
Il est toutefois principalement tourné vers PVM et MPI sur des <foreignphrase>clusters</foreignphrase>
de stations de travail, et peut fonctionner sous Linux. Plus d'informations sur
<ulink
url="http://www.cs.washington.edu/research/zpl/home/index.html"
>http://www.cs.washington.edu/research/zpl/home/index.html</ulink>.
</para>

</sect3>

</sect2>

<sect2>
<title>Question de performance</title>

<para>
Beaucoup de gens passent beaucoup de temps à faire des mesures de
performances <quote>au banc d'essai</quote> de cartes-mères particulières, de
carte réseau, et cætera, pour déterminer laquelle d'entre elles est la
meilleure. Le problème de cette approche est que, le temps d'arriver à des
résultats probants, le matériel testé peut ne plus être le meilleur.
Il peut même être retiré du marché et remplacé par un modèle révisé et
aux propriétés radicalement différentes.
</para>

<para>
Acheter du matériel pour PC, c'est un peu comme acheter du jus d'orange.
Il est généralement fabriqué à partir de produits de bonne qualité, et ce
quelque soit le nom de la compagnie qui le distribue. Peu de gens connaissent
ou se soucient de la provenance de ces composant (ou du concentré de jus
d'orange). Ceci dit, il existe quelques différences auxquelles on devrait
être attentif. Mon conseil est simple&nbsp;: Soyez simplement conscients de ce
que vous pouvez attendre de votre matériel lorsqu'il fonctionne sous Linux,
puis portez votre attention sur la rapidité de livraison, un prix raisonnable
et une garantie convenable.
</para>

<para>
Il existe un excellent aperçu des différents processeurs pour PC sur <ulink
url="http://www.pcguide.com/ref/cpu/fam/"
>http://www.pcguide.com/ref/cpu/fam/</ulink>; En fait, le site <ulink
url="http://www.pcguide.com/"
>PC Guide</ulink> entier est rempli de bonnes présentations techniques
de l'électronique d'un PC. Il est également utile d'en savoir un peu sur les
performances et la configuration d'un matériel spécifique, et le <ulink
url="&howto;Benchmarking-HOWTO.html"
>Linux&nbsp;Benchmarking&nbsp;HOWTO</ulink> est un bon document pour commencer.
</para>

<para>

Les processeurs Intel IA32 sont dotés de plusieurs registres spéciaux 
qui peuvent être utilisés pour mesurer les performances d'un système en 
fonction dans ses moindres détails. Le VTune d'Intel, sur <ulink 
url="http://developer.intel.com/design/perftool/vtune"/>, fait un usage 
poussé de ces registres de performances, dans un système d'optimisation 
du code vraiment très complet&hellip; qui malheureusement ne fonctionne 
pas sous Linux. Un pilote sous forme de module chargeable et une 
bibliothèque de fonctions servant à accéder aux registres de 
performances du Pentium sont disponibles et ont été écrits par Cuneyt 
Akinlar. Souvenez-vous que ces registres de performance sont différents 
selon les processeurs IA32. Ce code ne fonctionnera donc que sur un 
Pentium, pas sur un 486, ni sur un Pentium Pro, un Pentium II, un K6, et 
cætera.

</para>

<para>

Il y a encore un commentaire à faire à propos des performances, destiné 
spécialement à ceux qui veulent monter de grands 
<foreignphrase>clusters</foreignphrase> dans de petits espaces. Les 
processeurs modernes incorporent pour certains des capteurs de 
température, et utilisent des circuits ralentissant la cadence du 
processeur lorsque cette température dépasse un certain seuil (tentative 
de réduction des émissions de chaleur et d'amélioration de la 
fiabilité). Je ne suis pas en train de dire que tout le monde devrait 
sortir s'acheter un module à effet Peltier (ou <quote>pompe à 
chaleur</quote>) pour refroidir chaque CPU, mais que nous devrions être 
conscients du fait qu'une température trop élevé ne se contente pas de 
raccourcir la durée de vie des composants, mais agit aussi directement 
sur les performances d'un système. Ne placez vos ordinateurs dans des 
configurations pouvant bloquer le flux d'air, piégeant la chaleur dans 
des zones confinées, et cætera.

</para>

<para>

Enfin, les performances ne sont pas seulement une question de vitesse, 
mais également de fiabilité et de disponibilité. Une haute fiabilité 
signifie que votre système ne plantera pratiquement jamais, même si un 
composant vient à tomber en panne&hellip; ce qui nécessite généralement 
un matériel spécial comme une alimentation redondante et une carte-mère 
autorisant le remplacement <quote>à chaud</quote> des équipements qui y 
sont connectés. Tout cela est en général assez cher. Une haute 
disponibilité signifie que votre système sera prêt à être utilisé 
pratiquement tout le temps. Le système peut planter si l'un des 
composants tombe en panne, mais il pourra être réparé et redémarré très 
rapidement. Le High-Availability HOWTO traite plusieurs des cas de base 
de la haute disponibilité. En revanche, dans le cas des 
<foreignphrase>clusters</foreignphrase>, une haute disponibilité peut 
être assurée en prévoyant simplement quelques pièces de rechange. 
Éliminer le matériel défectueux et le remplacer par une de ces pièces de 
rechange peut apporter une plus grande disponibilité pour un coût 
d'entretien moins élevé que celui d'un contrat de maintenance.

</para>

</sect2>

<sect2>
<title>Conclusion &mdash; C'est fini&nbsp;!</title>

<para>
Alors, y a-t-il quelqu'un dans l'assemblée qui fasse du traitement en
parallèle sous Linux&nbsp;? Oui&nbsp;!
</para>

<para>

Il y a encore peu de temps, beaucoup de gens se demandaient si la mort 
de plusieurs compagnies fabricant des supercalculateurs parallèles 
n'annonçait pas la fin du traitement en parallèle. Ce n'était alors pas 
mon opinion (voir <ulink url="&page-auteur;Opinions/pardead.html"/> pour 
un aperçu assez amusant de ce qui, à mon avis, s'est vraiment passé), et 
il semble assez clair qu'aujourd'hui le traitement en parallèle est à 
nouveau sur une pente ascendante. Même Intel, qui n'a cessé que 
récemment de produire des supercalculateurs parallèles, est fier du 
soutien apporté au traitement en parallèle par des choses comme le MMX, 
et l'EPIC (<quote><foreignphrase>Explicitly Parallel Instruction 
Computer</foreignphrase></quote>, ou <quote>Ordinateur à jeu 
d'Instructions Parallèles Explicites</quote>) de l'IA64.

</para>

<para>

Si vous faites une recherche sur les mots <quote>Linux</quote> et 
<quote>Parallèle</quote> dans votre moteur de recherche favori, vous 
trouverez un certain nombre d'endroits impliqués dans le traitement en 
parallèle sous Linux. Les <foreignphrase>clusters</foreignphrase> de PC 
sous Linux, en particulier, font leur apparition un peu partout. Le fait 
que Linux se prête particulièrement bien à ce genre de tâche combiné aux 
coûts réduits et aux hautes performances du matériel pour PC ont fait du 
traitement en parallèle sous Linux une approche populaire, tant pour les 
groupes au budget restreint que pour les vastes laboratoires de 
recherche nationaux confortablement subventionnés.

</para>

<para>

Certains projets énumérés dans ce document tiennent une liste de sites 
de recherche <quote>apparentés</quote> utilisant des configurations 
Linux parallèles similaires. Vous trouvez cependant sur <ulink 
url="http://yara.ecn.purdue.edu/~pplinux/Sites"/> un document hypertexte 
dont le but est d'apporter photographies, descriptions et contacts vers 
les sites de tous les projets utilisant Linux pour effectuer des 
traitement en parallèle. Si vous voulez voir votre propre site y 
figurer&nbsp;:

</para>

<itemizedlist>

<listitem><para>

Vous devez avoir un site <quote>permanent</quote> présentant le 
fonctionnement d'un système Linux en parallèle&nbsp;: Une machine SMP, 
un système SWAR, ou un PC équipé de processeurs dédiés, configuré pour 
permettre aux utilisateurs d'<emphasis>exécuter des programmes 
parallèles sous Linux</emphasis>. Un environnement logiciel basé sur 
Linux et prenant directement en charge la gestion du traitement en 
parallèle (tel que PVM, MPI ou AFAPI) doit être installé sur le système. 
En revanche, le matériel ne doit pas nécessairement être dédié au 
traitement en parallèle sous Linux et peut être employé à des tâches 
complètement différentes lorsque les programmes parallèles ne sont pas 
en cours d'exécution.

</para></listitem>

<listitem><para>

Vous devez formuler une demande pour que votre site apparaisse sur cette 
liste. Envoyez vos informations en anglais à

<email>&addr-auteur;</email>.

Merci de respecter le format utilisé par les autres entrées pour les 
informations concernant votre site. <emphasis>Aucun site ne sera 
répertorié sans demande explicite de la part du responsable de ce 
site</emphasis>.

</para></listitem>

</itemizedlist>

<para>

Quatorze <foreignphrase>clusters</foreignphrase> sont actuellement 
répertoriés dans cette liste, mais nous avons été informés de 
l'existence de plusieurs douzaines de 
<foreignphrase>clusters</foreignphrase> Linux à travers le monde. Bien 
sûr, cette inscription n'implique aucun engagement. Notre objectif est 
simplement de favoriser le développement des connaissances, de la 
recherche et de la collaboration impliquant le traitement en parallèle 
sous Linux.

</para>

</sect2>

</sect1>

</article>

Site hébergé sur un Cloud Public IKOULA Ikoula