Un seul travail, une multitude de morceaux
Éclaircir une photo, c'est ajouter le même petit nombre à des millions de pixels. Un pixel ne dépend pas d'un autre : des milliers de petits ouvriers peuvent donc en prendre chacun quelques-uns. Une carte graphique est faite exactement pour cela. Ce lab n'a pas de carte graphique, mais il te laisse voir les idées sur lesquelles elle repose.
À la fin de cette leçon
Remarque
Je peux expliquer en quoi faire un même travail sur beaucoup de données à la fois diffère d'une boucle, et pourquoi plus d'ouvriers ne rendent pas le travail proportionnellement plus rapide.
Mots clés
- Parallèle : plusieurs parties d'un travail faites en même temps.
- Ouvrier (worker) : un processus ou un fil d'exécution qui fait une part du travail.
- Partie séquentielle : la part d'un travail qui ne peut pas être découpée et qui se fait dans l'ordre.
- Accélération : combien de fois le travail est plus rapide qu'avec un seul ouvrier.
- Fil d'exécution (thread) : un petit ouvrier ; un GPU en fait tourner des milliers à la fois.
- Noyau (kernel) : la petite fonction que chaque fil exécute, chacun sur son propre élément.
Pour commencer : ce que tu sais déjà
Remarque
Réponds dans ta tête. 1. Que fait une boucle for ? 2. Si une personne met 8 minutes à plier 80 tracts, combien de temps mettent 4 personnes si elles se partagent la pile à parts égales ? 3. Et si une des 8 minutes sert à écrire la liste des adresses, que seule une personne peut faire ? Réponses : elle répète les mêmes étapes l'une après l'autre ; 2 minutes ; plus de 2, car la liste des adresses prend toujours 1 minute pour une seule personne.
Avant de commencer
Remarque
Appuie sur Démarrer le lab dans la barre en haut de la leçon et attends qu'il indique qu'il est en marche. Il y a un seul conteneur, jupyter, et le terminal atteint son Python avec docker exec jupyter, qui lance une commande à l'intérieur. Le lien JupyterLab ouvre sa page web, sur le port 8888. Il n'y a pas de GPU ici. Chaque bouton Exécuter ci-dessous tape sa commande dans le terminal de ton lab, l'une après l'autre, dans la même session. Pour les suivre ou taper les tiennes, appuie sur Ouvrir le terminal (nouvel onglet) dans la barre.
Un projet parallèle, c'est un dossier de petits scripts
ls liste le dossier de travail : le notebook start.ipynb et le dossier steps. cat affiche le premier script. Il additionne deux fois les carrés de 1 à 1000 : une fois avec une boucle Python, une fois avec une seule opération NumPy, qui fait le même travail sur tout le tableau d'un coup.
docker exec jupyter lsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
docker exec jupyter cat steps/vectors.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une opération sur tout un tableau
Les deux méthodes donnent 333833500, et le script indique que les deux résultats sont égaux. La boucle demande 2000 opérations au niveau de Python, une multiplication et une addition pour chacun des 1000 nombres. NumPy en demande 1, car un seul appel fait tout le travail dans du code compilé rapide. Rien n'est chronométré : le script affiche donc la même chose à chaque fois. Un GPU va plus loin : une instruction, des milliers d'éléments.
docker exec jupyter python3 steps/vectors.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Partager un travail entre ouvriers
Les nombres de 1 à 40 sont coupés en 4 morceaux de 10, et deux ouvriers se les partagent. Chaque morceau donne une somme partielle, 55, 155, 255 et 355, toujours listées dans l'ordre des morceaux, et leur total fait 820, comme la somme de toute la liste d'un bloc. Le découpage marche parce que chaque morceau peut être additionné sans connaître les autres.
docker exec jupyter python3 steps/chunks.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
La limite de l'accélération
Une partie de tout travail ne peut pas être découpée. Le tableau calcule l'accélération avec 1, 2, 4 et 8 ouvriers quand 10, 25 et 50 pour cent ne le peuvent pas. Avec 10 pour cent de séquentiel, 8 ouvriers donnent 4,71 fois, pas 8, et même une infinité d'ouvriers ne donnent que 10,0 au plus. Avec 50 pour cent de séquentiel, 8 ouvriers ne donnent que 1,78.
docker exec jupyter python3 steps/amdahl.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Vérifie
Astuce
Dans le tableau, de combien le travail est-il plus rapide avec 8 ouvriers si 25 pour cent ne peuvent pas être découpés ? Réponse : 2,91 fois, pas 8, car le quart séquentiel est toujours fait par un seul ouvrier.
Un lancement de blocs de fils
Un GPU exécute une fonction appelée noyau une fois par fil. Les fils sont regroupés en blocs, et chacun calcule quel élément est le sien avec bloc * taille du bloc + fil. Ce script simule un lancement de 3 blocs de 4 fils sur 10 éléments, pour additionner deux vecteurs. Il affiche l'indice global de chaque fil, de 0 à 11, et le résultat 11, 22, et ainsi de suite jusqu'à 110. Les deux derniers fils, 10 et 11, dépassent la fin et ne font rien.
docker exec jupyter python3 steps/kernel.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Déplacer des données coûte aussi du temps
Un appareil a deux limites : la vitesse à laquelle il déplace des octets et celle à laquelle il fait des calculs. Ce script est de l'arithmétique sur un appareil inventé qui déplace 500 gigaoctets par seconde et fait 10 téraflops par seconde. Une addition de vecteurs de 10 millions d'éléments déplace 120000000 octets pour 10000000 calculs : elle est limitée par la mémoire. Une multiplication de matrices 1000 par 1000 déplace 12000000 octets pour 2000000000 calculs : elle est limitée par le calcul.
docker exec jupyter python3 steps/transfer.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
Beaucoup pensent que 8 ouvriers rendent un travail 8 fois plus rapide. C'est vrai seulement si rien du travail ne doit se faire dans l'ordre. Ce qui reste séquentiel fixe un plafond, et déplacer les données peut coûter plus cher que les calculs. Pose-toi deux questions avant d'ajouter des ouvriers : quelle part ne peut pas être découpée, et le travail attend-il la mémoire ou le calcul ?
Le même travail dans un notebook
Ouvre JupyterLab depuis la barre et double-clique sur start.ipynb. Chaque cellule lance un script : tu vois donc ce que faisaient les six scripts, une idée à la fois. Clique sur une cellule et appuie sur Maj et Entrée pour l'exécuter. Pour explorer, ouvre un script dans la liste des fichiers, change un nombre, par exemple le nombre de morceaux ou la part qui ne peut pas être découpée, et relance-le.
Vérification finale
Astuce
Réponds sans revenir en arrière. 1. Combien d'opérations au niveau de Python la boucle a-t-elle demandées pour la somme des carrés, et combien NumPy ? 2. À quoi les quatre sommes partielles ont-elles abouti ? 3. Avec 10 pour cent de séquentiel, quelle est l'accélération maximale possible, quel que soit le nombre d'ouvriers ? 4. Que calcule d'abord chaque fil d'un noyau ? Réponses : 2000 et 1 ; 820 ; 10,0 fois ; quel élément est le sien, son indice global.
Pour les enseignants
Astuce
Cette leçon est un petit exemple de leçon travaillée dans un lab : les étapes et les boutons Exécuter sont la leçon, et le lab est la copie de chaque élève. Le lab est un seul service, JupyterLab avec NumPy, et n'a pas de GPU : il enseigne le modèle sur lequel un GPU est construit, et rien ici ne se corrige tout seul. L'élève rend une courte modification à lui d'un script et une phrase sur ce qui a bougé. Points clés : 1. Une opération sur tout un tableau remplace une boucle. 2. Un travail partagé entre ouvriers garde une partie séquentielle qui fixe un plafond. 3. Un noyau s'exécute une fois par fil, et chaque fil calcule son propre élément. Erreur fréquente : croire que 8 ouvriers donnent 8 fois la vitesse. Matériel : le lab et un navigateur. Conseil : demandez aux élèves de prévoir chaque case du tableau avant d'appuyer sur Exécuter. Chaque script affiche la même chose à chaque fois, car rien n'est chronométré. Copiez-la dans votre propre module et modifiez-la, ou créez-en une de zéro avec le menu Lab du créateur de leçons.