Question

Qu'est-ce qu'un verrou interprète global et pourquoi est-il un problème?

Beaucoup de bruit a été fait autour de la suppression du GIL de Python, et je voudrais comprendre pourquoi cela est si important. Je ne l'ai jamais écrit un compilateur ni un interprète moi-même, ne, je ne sois pas avare de détails aurez probablement besoin de les comprendre.

Était-ce utile?

La solution

GIL de python est destiné à sérialiser l'accès aux équipements internes interpréteur de différents threads. Sur les systèmes multi-core, cela signifie que plusieurs threads ne peuvent pas utiliser efficacement plusieurs cœurs. (Si le GIL n'a pas abouti à ce problème, la plupart des gens ne se soucient pas du GIL - il est seulement d'être soulevé comme un problème en raison de la prévalence croissante des systèmes multi-core.) Si vous voulez comprendre en détail, vous pouvez voir cette vidéo ou regarder cette série de diapositives . Il est peut-être trop d'informations, mais vous ne demandez pour plus de détails: -)

Notez que GIL Python n'est vraiment un problème pour CPython, la mise en œuvre de référence. Jython et IronPython n'ont pas GIL. En tant que développeur Python, vous ne venez généralement pas dans le GIL sauf si vous écrivez une extension C. C écrivains d'extension doivent libérer le GIL lorsque leurs extensions de blocage du E / S, de sorte que d'autres threads du processus Python ont la chance de courir.

Autres conseils

Supposons que vous avez plusieurs threads qui ne sont pas vraiment toucher les données de chacun. Celles-ci devraient exécuter de façon aussi autonome que possible. Si vous avez un « verrou global » que vous devez acquérir pour (par exemple) appeler une fonction, qui peut finir par un goulot d'étranglement. Vous pouvez liquider ne pas avoir beaucoup d'avantages d'avoir plusieurs threads en premier lieu.

Pour le mettre en une véritable analogie mondiale: imaginez 100 développeurs travaillant dans une entreprise avec une seule tasse de café. La plupart des développeurs passeraient leur temps d'attente pour le café au lieu de coder.

Rien de tout cela est spécifique à Python - Je ne connais pas les détails de ce que Python avait besoin d'un GIL pour en premier lieu. Cependant, nous espérons qu'il vous a donné une meilleure idée du concept général.

Voyons d'abord comprendre ce que le python GIL fournit:

Toute opération / instruction est exécutée dans l'interpréteur. GIL assure que l'interprète est tenu par un seul fil à un instant particulier du temps . Et votre programme python avec plusieurs threads fonctionne dans un seul interprète. A tout instant particulier du temps, cet interprète est tenu par un seul fil. Cela signifie que seul le fil qui tient l'interprète est exécutant à tout instant .

Maintenant, pourquoi est-ce un problème:

Votre machine pourrait être d'avoir plusieurs cœurs / processeurs. Et plusieurs noyaux permettent plusieurs threads d'exécuter simultanément i.e. plusieurs threads peuvent exécuter à tout instant particulier du temps. . Mais puisque l'interprète est tenu par un seul fil, d'autres fils ne sont pas en train de faire quoi que ce soit, même si elles ont accès à un noyau. Donc, vous n'obtenez aucun avantage fourni par plusieurs cœurs, car à tout instant seulement un seul noyau, qui est le noyau utilisé par le fil qui tient actuellement l'interprète, est utilisé. Ainsi, votre programme prendra le temps d'exécuter comme si elle était un seul programme fileté.

Cependant, le blocage ou potentiellement des opérations de longue durée, tels que E / S, le traitement d'image, et le nombre NumPy crissement, arrive en dehors du GIL. Tiré de . Donc, pour ces opérations, une opération multithread sera encore plus rapide qu'une seule opération filetée malgré la présence de GIL. Donc, GIL est pas toujours un goulot d'étranglement.

Edit: GIL est un détail de mise en œuvre de CPython. IronPython et Jython n'ont pas GIL, donc un véritable programme multithread devrait être possible en eux, pensé que je ne l'ai jamais utilisé PyPy et Jython et pas sûr de cela.

Python ne permet pas le multi-threading dans le sens le plus vrai du mot. Il a un paquet multi-threading, mais si vous voulez multi-thread pour accélérer votre place de code, il est généralement pas une bonne idée de l'utiliser. Python a une construction appelée Global Interpreter Lock (GIL).

https://www.youtube.com/watch?v=ph374fJqFPE

Le GIL assure que seul un de vos « fils » peut exécuter à tout moment. Un fil acquiert le GIL, fait un peu de travail, puis passe le GIL sur le fil suivant. Cela se produit très rapidement, de sorte à l'oeil humain, il peut sembler que vos fils sont exécutaient en parallèle, mais ils sont vraiment juste à tour de rôle en utilisant le même noyau CPU. Tout cela passant GIL ajoute les frais généraux à l'exécution. Cela signifie que si vous voulez faire plus vite tourner votre code puis en utilisant le package de filetage est souvent pas une bonne idée.

Il y a des raisons d'utiliser le paquet de threads de Python. Si vous voulez exécuter certaines choses en même temps, et l'efficacité est pas un problème, alors il est tout à fait bien et pratique. Ou si vous exécutez du code qui doit attendre quelque chose (comme certains IO), il pourrait alors faire beaucoup de sens. Mais la bibliothèque de threads ne laissera pas utiliser des noyaux de CPU supplémentaires.

Multi-threading peut être sous-traitée au système d'exploitation (en faisant multitraitement), une application externe qui appelle votre code Python (par exemple, Spark ou Hadoop), ou un code que vos appels de code Python (par exemple: vous pourriez avoir votre code Python appel d'une fonction C qui fait le coûteux truc multi-thread).

Chaque fois que deux fils ont accès à la même variable que vous avez un problème. En C ++, par exemple, la façon d'éviter le problème est de définir une mutex pour éviter deux fils à, disons, entrez le poseur d'un objet en même temps.

multithreading est possible en python, mais deux fils ne peut pas être exécuté en même temps à une plus fine granularité d'une instruction de python. Le fil conducteur est d'obtenir un verrou global appelé GIL.

Cela signifie que si vous commencez à écrire du code multithread afin de tirer profit de votre processeur multi-cœurs, votre performance ne va pas améliorer. La solution habituelle consiste à aller multiprocessus.

Notez qu'il est possible de libérer le GIL si vous êtes à l'intérieur d'une méthode que vous avez écrit en C par exemple.

L'utilisation d'un GIL n'est pas inhérent à Python, mais à une partie de son interprète, y compris CPython le plus commun. (#Edited, voir le commentaire)

La question GIL est toujours valide en Python 3000.

Python 3.7 Documentation

Je voudrais aussi mettre en évidence la citation suivante de la documentation Python threading :

  

CPython détail de mise en œuvre: En CPython, en raison du Global Interpreter Lock, un seul thread peut exécuter du code Python à la fois (même si certaines bibliothèques axées sur les performances pourraient surmonter cette limitation). Si vous voulez que votre application pour faire une meilleure utilisation des ressources informatiques des machines multi-core, il est conseillé d'utiliser multiprocessing ou concurrent.futures.ProcessPoolExecutor. Cependant, le filetage est toujours un modèle approprié si vous voulez exécuter plusieurs tâches I / O-lié simultanément.

Ce lien renvoie vers entrée Glossaire pour global interpreter lock ce qui explique que le GIL implique que le parallélisme fileté en Python ne convient pas pour tâches liées CPU :

  

Le mécanisme utilisé par l'interpréteur CPython pour assurer qu'un seul thread exécute python bytecode à la fois. Cela simplifie la mise en œuvre CPython en faisant le modèle objet (y compris les types intégrés critiques tels que dict) implicitement de sécurité contre l'accès simultané. Verrouillage de l'interprète ensemble facilite l'interprète d'être multithread, au détriment d'une grande partie du parallélisme offert par les machines multi-processeurs.

     

Cependant, certains modules d'extension, soit standard ou tiers, sont conçus de manière à libérer le GIL en accomplissant les tâches informatiquement intensives telles que la compression ou le hachage. En outre, le GIL est toujours libéré lorsque vous faites E / S.

     

Après les efforts visant à créer un interprète « free-thread » (une qui verrouille des données partagées sur une granularité plus fine) n'ont pas été couronnées de succès parce que la performance a souffert dans le cas monoprocesseur commun. On croit que surmonter ce problème de performance rendrait la mise en œuvre beaucoup plus complexe et donc plus coûteux à maintenir.

Cette citation implique également que dicts et donc l'affectation des variables sont thread également en sécurité comme un détail de mise en œuvre CPython:

Ensuite, les docs pour le package multiprocessing expliquer comment surmonte la GIL par processus de reproduction tout en exposant une interface similaire à celle de threading:

  

multitraitement est un paquet qui prend en charge les processus de reproduction à l'aide d'une API similaire au module de filetage. Le forfait multitraitement offre à la fois local et à distance concurrency, Esquive efficacement Global Interpreter Lock en utilisant au lieu de fils sous-processus. En raison de cela, le module multitraitement permet au programmeur de tirer pleinement parti de plusieurs processeurs sur une machine donnée. Il fonctionne aussi bien sur Unix et Windows.

Et les docs pour concurrent.futures.ProcessPoolExecutor expliquent qu'il utilise multiprocessing comme un arrière-plan:

  

La classe ProcessPoolExecutor est une sous-classe Executor qui utilise un pool de processus pour exécuter des appels de manière asynchrone. ProcessPoolExecutor utilise le module multiprocesseur, ce qui lui permet pas de côté le GLOBAL interprète de verrouillage mais aussi des moyens qui peuvent être exécutés et retournés objets que picklable.

qui doit être opposée à l'autre ThreadPoolExecutor de classe de base qui utilise des fils au lieu des processus

  

ThreadPoolExecutor est une sous-classe d'exécuteur qui utilise un pool de threads pour exécuter des appels de façon asynchrone.

à partir de laquelle nous concluons que ThreadPoolExecutor ne convient que pour des tâches liées E / S, tandis que ProcessPoolExecutor peut également gérer des tâches liées CPU.

La question suivante demande pourquoi le GIL existe en premier lieu: Pourquoi Global Interpreter Lock?

Processus vs expériences de fil

multitraitement vs Threading Python je l'ai fait une analyse expérimentale procédé vs fils dans python.

Aperçu rapide des résultats:

Pourquoi Python (CPython et d'autres) utilise le GIL

De http://wiki.python.org/moin/GlobalInterpreterLock

Dans CPython, le verrou de l'interpréteur global ou GIL, est un mutex qui empêche plusieurs threads natifs d'exécuter bytecode Python à la fois. Cette serrure est nécessaire principalement parce que la gestion de la mémoire de CPython n'est pas thread-safe.

Comment supprimer de Python?

Comme Lua, peut-être Python pourrait lancer plusieurs VM, mais python ne le fait pas, je suppose qu'il devrait y avoir d'autres raisons.

Dans Numpy ou une autre bibliothèque étendue de python, parfois, libérant le GIL à d'autres fils pourrait augmenter l'efficacité du programme.

Je veux partager un exemple du multithreading livre pour effets visuels. Voici donc une situation de blocage mort classique

static void MyCallback(const Context &context){
Auto<Lock> lock(GetMyMutexFromContext(context));
...
EvalMyPythonString(str); //A function that takes the GIL
...    
}

Considérons maintenant les événements de la séquence résultant d'un verrouillage mort.

╔═══╦════════════════════════════════════════╦══════════════════════════════════════╗
║   ║ Main Thread                            ║ Other Thread                         ║
╠═══╬════════════════════════════════════════╬══════════════════════════════════════╣
║ 1 ║ Python Command acquires GIL            ║ Work started                         ║
║ 2 ║ Computation requested                  ║ MyCallback runs and acquires MyMutex ║
║ 3 ║                                        ║ MyCallback now waits for GIL         ║
║ 4 ║ MyCallback runs and waits for MyMutex  ║ waiting for GIL                      ║
╚═══╩════════════════════════════════════════╩══════════════════════════════════════╝
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top