Ce tutoriel est une traduction infidèle d'un article de realpython.com https://realpython.com/python-concurrency/#when-to-use-concurrency
Merci à eux pour leur formidable travail et leur autorisation.
Vous avez certainement entendu parler de la librairie asyncio qui a été ajouté à Python 3 et vous êtes curieux de savoir comment elle se place par rapport aux autres méthodes de programmations concurrentes ? Vous voulez savoir ce qu'est la programmation concurrente et comment cela pourrait accélérer vos programmes ? Vos données sont trop grosses et vos calculs ou vos requêtes prennent des heures ? Vous êtes au bon endroit !
Dans ce tutoriel nous allons voir :
- ce qu'est la programmation concurrente
- ce qu'est la parallélisation
- les différence entre les méthodes de programmation concurrente (threading, asyncio et multiprocessing)
- comment utiliser la programmation concurrente dans vos programmes.
Évidement la parallélisation ne remplacera jamais une bonne optimisation algorithmique de votre code et un code bien optimisé ira toujours plus vite une fois parallélisé qu'un code mal optimisé.
Ceci dit, entrons sans plus attendre dans le monde merveilleux de la programmation concurrente.
Qu'est-ce que la concurrence (ou programmation concurrente) ?
La concurrence c'est l'occurrence simultanée de plusieurs événements. En Python, les choses qui se produisent simultanément sont appelées par des noms différents (thread, tâche, processus), mais à un niveau élevé
elles font toutes référence à une séquence d'instructions qui s'exécutent dans l'ordre. On pourrait les considérer comme des courants de pensées différents. Chacun d'entre eux peut être arrêté à certains endroits,
et le processeur ou le cerveau qui les traite peut passer à un autre. L'état de chacun d'eux est sauvegardé pour pouvoir être redémarré là où il a été interrompu.
Vous vous demandez peut-être pourquoi Python utilise des mots différents pour le même concept. Il s'avère que les threads, les tasks et les processus ne sont identiques que si vous les visualisez à haut niveau. Une fois que vous commencez à fouiller dans les détails, ils représentent tous des choses légèrement différentes. Vous verrez de plus en plus en quoi ils sont différents au fur et à mesure que vous progressez dans les exemples.
Parlons maintenant de la partie simultanée de cette définition. Il faut être prudent parce que, dans les détails, seul le multiprocessing fait fonctionner ces processus en même temps. Le multithreading et l'asyncio tournent tous deux sur un seul processeur et ne fonctionnent donc pas réellement en parallèle. Ils trouvent simplement des moyens intelligents de se relayer pour accélérer le processus global. Même s'ils n'exécutent pas différents courants de pensées simultanément, nous appelons toujours cela la concurrence.
La façon dont les threads ou les tasks se succèdent est la grande différence entre le multithreading et l'asyncio. Dans le multithreading, le système d'exploitation connaît réellement chaque thread et peut l'interrompre à tout moment pour lancer un thread différent. C'est ce qu'on appelle le "pre-emptive multitasking" car le système d'exploitation peut reprendre la main sur votre thread pour effectuer le changement.
Le "pre-emptive multitasking" est pratique dans la mesure où le code dans le thread n'a pas besoin de faire quoi que ce soit pour effectuer le changement. Cela peut aussi être difficile à cause de cette phrase "à tout moment". Ce changement peut se produire au milieu d'une seule instruction Python, même une instruction triviale comme x = x + 1.
Asyncio, par contre, utilise le "cooperative multitasking". Les tâches doivent coopérer en annonçant quand elles sont prêtes à être remplacées. Cela signifie que le code de la tâche doit être légèrement modifié pour que cela se produise.
L'avantage d'effectuer ce travail supplémentaire dès le départ est que vous savez toujours où votre tâche sera échangée. Il ne sera pas échangé au milieu d'une instruction Python à moins que cette instruction ne soit marquée. Vous verrez plus loin comment cela peut simplifier certaines parties de votre conception.
Qu'est-ce que le parallélisme ?
Jusqu'à présent, vous avez examiné la concurrence qui se produit sur un seul processeur. Qu'en est-il de tous ces cœurs de CPU que votre nouvel ordinateur portable possède ? Comment les utiliser ? Le multiprocessing est la réponse.
Avec le multiprocessing, Python crée de nouveaux processus. Un processus ici peut être considéré comme un programme presque complètement différent, bien que techniquement il soit généralement défini comme une collection de ressources incluant de la mémoire, des gestionnaires de fichiers et d'autres choses. Une façon d'y penser est que chaque processus s'exécute dans son propre interpréteur Python.
Parce qu'il s'agit de processus différents, chacun de vos processus dans un programme multiprocesseur peut fonctionner sur un noyau différent. Fonctionner sur un cœur différent signifie qu'ils peuvent réellement fonctionner en même temps, ce qui est fabuleux. Il y a quelques complications qui découlent de procéder comme cela, mais Python les gère bien la plupart du temps.
différences concurrences parallélisme :
Type de programmation concurrente | Décision de changement | Nombre de processeurs |
pre-emptive multitasking (multithreading) | le système d'exploitation décide quand changer de thread | 1 |
comprehensive multitasking (asyncio) | les tache décident quand rendre le contrôle | 1 |
multiprocessing | les processus tournent en parallèle sur différents processeurs* | beaucoup |
Thread versus Processus
Voyons maintenant en pratique les points communs et différences entre Thread et Processus.
Un processus est une abstraction simple du système d'exploitation. C'est un programme qui est lui-même une exécution — en d'autres termes, du code qui fait tourner un autre code. De nombreux processus tournent toujours dans un ordinateur, et s'exécutent en parallèle.
Un processus peut contenir plusieurs tâches. Ils exécutent le même code appartenant au processus parent. Idéalement, ils tournent en parallèle, mais pas nécessairement. Les processus sont insuffisants parce que les applications nécessitent d'être attentif aux actions des utilisateurs, tout en mettant à jour l'affichage et en sauvegardant un fichier.
différences pratiques entre thread et processus :
PROCESSUS | THREAD |
Les processus ne partagent pas la mémoire | Les tâches partagent la mémoire |
Multiplier/échanger de processus est onéreux | Multiplier/échanger des tâches est moins coûteux |
Les processus demande plus de ressources | Les tâches sont moins gourmandes en ressource (on les appelle parfois des processus légers) |
Pas besoin de synchroniser la mémoire | Vous aurez besoin de mécanismes de synchronisation si vous souhaitez manipuler correctement la donnée |
Les taches de la programmation asynchrone ne sont pas présentes dans ce tableau. On peut dire que selon le contexte, leurs caractéristiques et leur utilité, sont pratiquement les mêmes que si on avait utilisé des threads. Le coût en ressources reste moindre que les threads et surtout que c'est la tâche elle-même qui décide de rendre ou non le contrôle et non pas le système d'exploitation, ne nécessitant pas d'action de la part du système d'exploitation.
Chaque type de programmation concurrente a son utilité. Nous allons maintenant voir quel type de programme ils peuvent améliorer.
Quand la concurrence est-elle utile ?
La programmation concurrente peut faire une grande différence pour deux types de problèmes. Ces problèmes sont généralement appelés "limités par le CPU" et "limités par les entrées/sorties (E/S)".
Les problèmes limités aux entrée/sortie (E/S) ralentissent votre programme parce qu'il doit souvent attendre l'E/S d'une ressource externe. Ils surviennent fréquemment lorsque votre programme travaille avec des choses qui sont beaucoup plus lentes que votre CPU .
Les exemples de choses qui sont plus lentes que votre CPU sont légion, mais heureusement votre programme n'interagit pas avec la plupart d'entre eux. Les choses lentes avec lesquelles votre programme interagira le plus souvent sont le système de fichiers et les connexions réseau (disque dure, NAS, base de données , requête http).
Voyons à quoi cela ressemble :
Diagramme de temps d'un programme limité par les E/S :
Dans le diagramme ci-dessus, les cases bleues indiquent le temps pendant lequel votre programme travaille, et les cases rouges indiquent le temps passé à attendre qu'une opération d'E/S soit terminée. Ce diagramme n'est pas à l'échelle car les requêtes sur Internet peuvent prendre plusieurs ordres de grandeur plus longs que les instructions CPU, de sorte que votre programme puisse passer la plupart de son temps à attendre. C'est ce que fait votre navigateur web la plupart du temps.
D'un autre côté, il y a des classes de programmes qui font des calculs significatifs sans parler au réseau ou accéder à un fichier. Ce sont les programmes limités par le CPU parce que la ressource limitant la vitesse de votre programme est le CPU et non pas le réseau ou le système de fichiers.
Voici un diagramme correspondant pour un programme lié au CPU :
Dans les prochains articles, vous verrez que les différentes formes de concurrences fonctionnent mieux ou moins bien avec les programmes liés au CPU et les programmes liés aux E/S. L'ajout de la concurrence à votre programme ajoute des options et des complications, vous devrez donc décider si l'accélération potentielle vaut la peine d'un effort supplémentaire. D'ici la fin de ce tutoriel, vous devriez avoir suffisamment d'informations pour commencer à prendre cette décision.
problème limité par les entrées/sorties vs problème limité par le CPU :
Problème limité par les entrées/sortie (IO bound) | Problème limité par le CPU (CPU bound) |
Le programme communique avec un périphérique lent comme une connexion réseau, un disque dur ou une imprimante | Le programme fait majoritairement de opération de calcul CPU |
L’accélérer implique majoritairement de superposer les temps d'attente des périphériques | L’accélérer implique de faire plus de calculs dans une même quantité de temps |
Dans le prochain article, nous examinerons les programmes limités par les E/S. Ensuite, nous verrons les programmes limités par CPU. Et pour finir quelque piste sur comment utiliser la concurrence pour accélérer votre programme python.
Merci a Yoann M, WJ et Kevin Gueuti pour la relecture attentive.
- *différents types de concurrence
Laisser un commentaire