Qu'est-ce qu'un verrou d'interpréteur global et pourquoi est-ce un problème?
Beaucoup de bruit a été fait autour de la suppression du GIL de Python, et j'aimerais comprendre pourquoi c'est si important. Je n'ai jamais écrit de compilateur ni d'interprète moi-même, alors ne soyez pas frugal avec les détails, j'en aurai probablement besoin pour comprendre.
python
python-internals
gil
e-satis
la source
la source
Réponses:
Le GIL de Python est destiné à sérialiser l'accès aux interpréteurs internes de différents threads. Sur les systèmes multicœurs, cela signifie que plusieurs threads ne peuvent pas utiliser efficacement plusieurs cœurs. (Si le GIL n'a pas conduit à ce problème, la plupart des gens ne se soucieraient pas du GIL - il est uniquement soulevé comme un problème en raison de la prévalence croissante des systèmes multicœurs.) Si vous voulez le comprendre en détail, vous pouvez visionner cette vidéo ou regarder cet ensemble de diapositives . C'est peut-être trop d'informations, mais vous avez demandé des détails :-)
Notez que GIL de Python n'est vraiment un problème que pour CPython, l'implémentation de référence. Jython et IronPython n'ont pas de GIL. En tant que développeur Python, vous ne rencontrez généralement pas le GIL sauf si vous écrivez une extension C. Les rédacteurs d'extensions C doivent libérer le GIL lorsque leurs extensions bloquent les E / S, afin que les autres threads du processus Python puissent s'exécuter.
la source
regex
,lxml
,numpy
modules. Cython permet de publier GIL en code personnalisé, par exemple,b2a_bin(data)
Supposons que vous ayez plusieurs threads qui ne sont pas vraiment touchent les données les uns des autres. Ceux-ci devraient s'exécuter de manière aussi indépendante que possible. Si vous avez un "verrou global" que vous devez acquérir afin (par exemple) d'appeler une fonction, cela peut finir comme un goulot d'étranglement. Vous pouvez finir par ne pas tirer beaucoup d'avantages d'avoir plusieurs threads en premier lieu.
Pour le mettre dans une analogie réelle: imaginez 100 développeurs travaillant dans une entreprise avec une seule tasse de café. La plupart des développeurs passeraient leur temps à attendre le café au lieu de coder.
Rien de tout cela n'est spécifique à Python - je ne connais pas les détails de ce que Python avait besoin d'un GIL en premier lieu. Cependant, j'espère que cela vous a donné une meilleure idée du concept général.
la source
Commençons par comprendre ce que le python GIL fournit:
Toute opération / instruction est exécutée dans l'interpréteur. GIL garantit que l'interpréteur est détenu par un seul thread à un instant donné . Et votre programme python avec plusieurs threads fonctionne dans un seul interpréteur. À tout instant particulier, cet interprète est détenu par un seul thread. Cela signifie que seul le thread qui contient l'interpréteur s'exécute à tout instant .
Maintenant, pourquoi est-ce un problème:
Votre machine peut avoir plusieurs cœurs / processeurs. Et plusieurs cœurs permettent à plusieurs threads de s'exécuter simultanément, c'est-à-dire que plusieurs threads pourraient s'exécuter à tout instant particulier.. Mais puisque l'interpréteur est détenu par un seul thread, les autres threads ne font rien même s'ils ont accès à un noyau. Ainsi, vous n'obtenez aucun avantage fourni par plusieurs cœurs, car à tout instant, un seul cœur, qui est le cœur utilisé par le thread contenant actuellement l'interpréteur, est utilisé. Ainsi, votre programme prendra autant de temps à exécuter que s'il s'agissait d'un programme à thread unique.
Cependant, des opérations potentiellement bloquantes ou de longue durée, telles que les E / S, le traitement d'image et la compression des nombres NumPy, se produisent en dehors du GIL. Pris d' ici . Ainsi, pour de telles opérations, une opération multithread sera toujours plus rapide qu'une opération à thread unique malgré la présence de GIL. Ainsi, GIL n'est pas toujours un goulot d'étranglement.
Edit: GIL est un détail d'implémentation de CPython. IronPython et Jython n'ont pas GIL, donc un programme vraiment multithread devrait être possible en eux, pensant que je n'ai jamais utilisé PyPy et Jython et que je n'en suis pas sûr.
la source
Python n'autorise pas le multi-threading dans le vrai sens du mot. Il a un package multi-thread mais si vous voulez multi-thread pour accélérer votre code, alors ce n'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 s'assure qu'un seul de vos «threads» peut s'exécuter à la fois. Un thread acquiert le GIL, fait un peu de travail, puis passe le GIL sur le thread suivant. Cela se produit très rapidement, donc à l'œil humain, il peut sembler que vos threads s'exécutent en parallèle, mais ils se contentent de tourner à tour de rôle avec le même cœur de processeur. Tout ce passage GIL ajoute des frais généraux à l'exécution. Cela signifie que si vous souhaitez accélérer l'exécution de votre code, l'utilisation du package de thread n'est souvent pas une bonne idée.
Il y a des raisons d'utiliser le package de threading de Python. Si vous voulez exécuter certaines choses simultanément et que l'efficacité n'est pas un problème, c'est tout à fait correct et pratique. Ou si vous exécutez du code qui doit attendre quelque chose (comme certains IO), cela pourrait avoir beaucoup de sens. Mais la bibliothèque de threads ne vous permettra pas d'utiliser des cœurs de processeur supplémentaires.
Le multi-threading peut être externalisé vers le système d'exploitation (en effectuant un multi-traitement), une application externe qui appelle votre code Python (par exemple, Spark ou Hadoop), ou du code que votre code Python appelle (par exemple: vous pourriez avoir votre Python appelez une fonction C qui fait les choses coûteuses multi-thread).
la source
Chaque fois que deux threads ont accès à la même variable, vous avez un problème. En C ++ par exemple, la façon d'éviter le problème est de définir un verrou mutex pour empêcher deux threads d'entrer, disons, en même temps dans le setter d'un objet.
Le multithreading est possible en python, mais deux threads ne peuvent pas être exécutés en même temps à une granularité plus fine qu'une instruction python. Le thread en cours d'exécution obtient un verrou global appelé GIL.
Cela signifie que si vous commencez à écrire du code multithread afin de profiter de votre processeur multicœur, vos performances ne s'amélioreront pas. La solution de contournement habituelle consiste à passer par plusieurs processus.
Notez qu'il est possible de libérer le GIL si vous êtes dans une méthode que vous avez écrite en C par exemple.
L'utilisation d'un GIL n'est pas inhérente à Python mais à certains de ses interprètes, y compris le CPython le plus courant. (# édité, voir commentaire)
Le problème GIL est toujours valide dans Python 3000.
la source
Documentation Python 3.7
Je voudrais également souligner la citation suivante de la documentation Python
threading
:Ce lien renvoie à l' entrée
global interpreter lock
du glossaire qui explique que le GIL implique que le parallélisme fileté en Python ne convient pas aux tâches liées au processeur :Cette citation implique également que les dicts et donc l'affectation des variables sont également thread-safe en tant que détail d'implémentation CPython:
Ensuite, les documents du
multiprocessing
package expliquent comment il surmonte le GIL en générant un processus tout en exposant une interface similaire à celle dethreading
:Et les documents
concurrent.futures.ProcessPoolExecutor
expliquent qu'il utilisemultiprocessing
comme backend:qui devrait être contrasté avec l'autre classe de base
ThreadPoolExecutor
qui utilise des threads au lieu de processusd'où nous concluons qu'il
ThreadPoolExecutor
ne convient qu'aux tâches liées aux E / S, tout enProcessPoolExecutor
pouvant également gérer les tâches liées au processeur.La question suivante demande pourquoi le GIL existe en premier lieu: pourquoi le verrouillage d'interprète global?
Expériences processus vs thread
Chez Multiprocessing vs Threading Python, j'ai fait une analyse expérimentale des processus vs threads en Python.
Aperçu rapide des résultats:
la source
Pourquoi Python (CPython et autres) utilise le GIL
Depuis http://wiki.python.org/moin/GlobalInterpreterLock
Dans CPython, le verrou d'interpréteur global, ou GIL, est un mutex qui empêche plusieurs threads natifs d'exécuter des bytecodes Python à la fois. Ce verrou est nécessaire principalement parce que la gestion de la mémoire de CPython n'est pas thread-safe.
Comment le supprimer de Python?
Comme Lua, peut-être que Python pourrait démarrer plusieurs VM, mais python ne fait pas cela, je suppose qu'il devrait y avoir d'autres raisons.
Dans Numpy ou une autre bibliothèque étendue python, parfois, la libération de GIL sur d'autres threads pourrait augmenter l'efficacité de l'ensemble du programme.
la source
Je veux partager un exemple du livre multithreading pour Visual Effects. Voici donc une situation de blocage classique
Considérez maintenant les événements de la séquence résultant d'un blocage.
la source