Intent to approve PEP 703: making the GIL optional(discuss.python.org)
discuss.python.org
Intent to approve PEP 703: making the GIL optional
https://discuss.python.org/t/a-steering-council-notice-about-pep-703-making-the-global-interpreter-lock-optional-in-cpython/30474
499 comments
Exciting. Python is mostly written as C shared libraries that knew they had a global lock to rely on.
Some of those do sufficiently simple things that they can run without any locking and all will be fine.
Others will still need locking, but are now under pressure to run without the gil. Some of those are going to do DIY locking within their own bounds.
Maybe what python has really been missing all these years is loads of ad hoc mutex calls scattered across the ecosystem. Data races and deadlocks introduced in the name of performance is not how I expected python to go out.
edit: expanding on this pessimism a bit.
Making C libraries written assuming a global lock thread safe is the sort of thing I'd expect concurrency experts to advise against and then make mistakes while implementing it. My working theory is that most people who wrote C extensions for python are not concurrency experts and are great programmers who won't back down from a challenge.
The data-race/hang/segfault consequences of this combination look totally inevitable to me. Python application developers are not going to love the new experience and I'm thankful my products are not built on top of the python ecosystem.
Some of those do sufficiently simple things that they can run without any locking and all will be fine.
Others will still need locking, but are now under pressure to run without the gil. Some of those are going to do DIY locking within their own bounds.
Maybe what python has really been missing all these years is loads of ad hoc mutex calls scattered across the ecosystem. Data races and deadlocks introduced in the name of performance is not how I expected python to go out.
edit: expanding on this pessimism a bit.
Making C libraries written assuming a global lock thread safe is the sort of thing I'd expect concurrency experts to advise against and then make mistakes while implementing it. My working theory is that most people who wrote C extensions for python are not concurrency experts and are great programmers who won't back down from a challenge.
The data-race/hang/segfault consequences of this combination look totally inevitable to me. Python application developers are not going to love the new experience and I'm thankful my products are not built on top of the python ecosystem.
Naiive question: Who needs No-GIL when we have asyncio and multiprocessing packages ?
never ever had a problem with GIL in python, always found a workaround just by spinning up ThreadPool or ProcessPool, and used async libraries when needed.
is there any use case of No-GIL which is not solved by multiprocessing ?
I thought Single threaded execution without overhead for concurrency primitives is the best way to high performance computing (as demonstrated by LMAX Disruptor)
never ever had a problem with GIL in python, always found a workaround just by spinning up ThreadPool or ProcessPool, and used async libraries when needed.
is there any use case of No-GIL which is not solved by multiprocessing ?
I thought Single threaded execution without overhead for concurrency primitives is the best way to high performance computing (as demonstrated by LMAX Disruptor)
This can (and I think will) cause issues for C extensions because many are written without multi-threading in mind. Here is a small example which is unsafe if lst can be accessed from another thread: https://news.ycombinator.com/item?id=36649769 Note that the code may cause a context switch even today if the C code callbacks into Python bytecode (via a __del__ method) and the bytecode is long enough (100 instructions I think). However, that is extremely unlikely and much C extension code is not written with such situations in mind.
People using C extensions may also rely on them executing atomically. For example, you could have a thread pool that posts and receives from a numpy array. Would work fine today but break without the GIL.
People using C extensions may also rely on them executing atomically. For example, you could have a thread pool that posts and receives from a numpy array. Would work fine today but break without the GIL.
Remember the transition of text to Unicode? 32 to 64-bit? Intel to ARM? Y2K?
No-GIL is a much smaller shift. It can follow the same transition path without radically breaking things. And if some things do break, there would be a well-defined way to handle those cases.
We all somehow survived those. Glad to see forward motion on this. It will open up a lot more terrain that has been marked off as untenable.
One of the things about early Swift that they got right was building breaking changes into the promise. Everyone knew where they stood and adjusted just fine. Sometimes I wish Python would take the same path.
No-GIL is a much smaller shift. It can follow the same transition path without radically breaking things. And if some things do break, there would be a well-defined way to handle those cases.
We all somehow survived those. Glad to see forward motion on this. It will open up a lot more terrain that has been marked off as untenable.
One of the things about early Swift that they got right was building breaking changes into the promise. Everyone knew where they stood and adjusted just fine. Sometimes I wish Python would take the same path.
I know they say specifically that they don't want a repeat of the Python3 transition scenario, but the approach they're taking now still veers eerily close to that path, at least it looks that way to me.
A lot will depend on the Python community and the distribution channels. I could see the community struggling to adopt it in a timely fashion, or distributions jumping the gun (Ubuntu, Fedora, Anaconda). Maybe it's too early to make hard decisions, but how much control does the SC really have to avoid such a scenario?
A lot will depend on the Python community and the distribution channels. I could see the community struggling to adopt it in a timely fashion, or distributions jumping the gun (Ubuntu, Fedora, Anaconda). Maybe it's too early to make hard decisions, but how much control does the SC really have to avoid such a scenario?
I’m glad they’re very conscious about how easily this could turn into a Python 4 debacle.
They’ll have to be intensely careful not to accidentally affect yes-GIL behaviour. All kinds of weird cases are possible if any sort of emulated GIL isn’t exactly like with a GIL.
They’ll have to be intensely careful not to accidentally affect yes-GIL behaviour. All kinds of weird cases are possible if any sort of emulated GIL isn’t exactly like with a GIL.
GIL: Global Interpreter Lock.
Good explanation here: https://realpython.com/python-gil/
Good explanation here: https://realpython.com/python-gil/
Two major problems here:
1. There are some improvements worth breaking reverse-compatibility for, and removing the GIL is such an improvement. Whether the changes in Python 3 were worth making breaking changes for is debatable: certainly I don't see "print" being a function as particularly valuable. But the flipside is that the 2-to-3 transition was overblown by a vocal minority. I've transitioned more than 5 codebases from 2 to 3, and in most cases, there were few problems. Most problems were with codebases where previous developers had pulled in libraries for everything, resulting in an amalgamation of abandoned libraries, but these codebases run into problems even without the core language breaking compatibility. The answer isn't to flame your language into never breaking compatibility, it's to not import all of pip and expect that to be a sustainable strategy.
The situation we have now is that the steering committee has received so much heat from the vocal minority that they're terrified to make breaking changes. But removing the GIL should be a breaking change. It's too fundamental to how Python works to not be. So they're trying to remove the GIL and make it not a breaking change, which is a bad idea, because it is ultimately going to be a breaking change. It would be much better to admit this is a breaking change and start working on the transition plan, than to try the impossible task of making it not breaking because you're too terrified of your users to admit the truth.
We've already seen this in Python 3.11 which broke code in my codebase. The changes to fix the breakage weren't hard, but I would have liked better communication that this might happen. But I also understand why this was hidden in a deprecation warning in a minor release rather than publicized, because the Python team is probably tired of being flamed for making breaking changes.
2. The more fundamental problem here is that a lot of other features of Python were built around the GIL. Most obviously, the async paradigms makes sense largely because of the GIL. Sans-GIL, it looks like in retrospect a send/recv actor model a la Erlang would have been a much better way forward. It's not really possible to reverse this, and this might be pushing Python toward a less cohesive set of features that don't really make sense together. This makes it feel like this is too little too late.
1. There are some improvements worth breaking reverse-compatibility for, and removing the GIL is such an improvement. Whether the changes in Python 3 were worth making breaking changes for is debatable: certainly I don't see "print" being a function as particularly valuable. But the flipside is that the 2-to-3 transition was overblown by a vocal minority. I've transitioned more than 5 codebases from 2 to 3, and in most cases, there were few problems. Most problems were with codebases where previous developers had pulled in libraries for everything, resulting in an amalgamation of abandoned libraries, but these codebases run into problems even without the core language breaking compatibility. The answer isn't to flame your language into never breaking compatibility, it's to not import all of pip and expect that to be a sustainable strategy.
The situation we have now is that the steering committee has received so much heat from the vocal minority that they're terrified to make breaking changes. But removing the GIL should be a breaking change. It's too fundamental to how Python works to not be. So they're trying to remove the GIL and make it not a breaking change, which is a bad idea, because it is ultimately going to be a breaking change. It would be much better to admit this is a breaking change and start working on the transition plan, than to try the impossible task of making it not breaking because you're too terrified of your users to admit the truth.
We've already seen this in Python 3.11 which broke code in my codebase. The changes to fix the breakage weren't hard, but I would have liked better communication that this might happen. But I also understand why this was hidden in a deprecation warning in a minor release rather than publicized, because the Python team is probably tired of being flamed for making breaking changes.
2. The more fundamental problem here is that a lot of other features of Python were built around the GIL. Most obviously, the async paradigms makes sense largely because of the GIL. Sans-GIL, it looks like in retrospect a send/recv actor model a la Erlang would have been a much better way forward. It's not really possible to reverse this, and this might be pushing Python toward a less cohesive set of features that don't really make sense together. This makes it feel like this is too little too late.
Thank you so much Python core developers and steering council. Python is one of my favourite languages along with Java and C.
I greatly welcome true multithreading in Python.
I use both multiprocessing and multithreading in Python for different projects. See [0] for my multiprocessing example and python Threads for IO heavy tasks in [1]. But it would be far more efficient to use true threads.
Threads can communicate any amount of data in a single atomic almost instant operation. Using the local loopback interface or multiprocessing or pipes, this is not possible.
I am working on a multithreading architecture I call three tier multithreading architecture
https://github.com/samsquire/three-tier-multithreaded-archit...
My goal is extremely scalable and performant servers but Python is probably the wrong job for that.
[0]: https://news.ycombinator.com/item?id=36897054 (my description of my use of multiprocessing) [1]: https://devops-pipeline.com/ (my use of multithreading)
I greatly welcome true multithreading in Python.
I use both multiprocessing and multithreading in Python for different projects. See [0] for my multiprocessing example and python Threads for IO heavy tasks in [1]. But it would be far more efficient to use true threads.
Threads can communicate any amount of data in a single atomic almost instant operation. Using the local loopback interface or multiprocessing or pipes, this is not possible.
I am working on a multithreading architecture I call three tier multithreading architecture
https://github.com/samsquire/three-tier-multithreaded-archit...
My goal is extremely scalable and performant servers but Python is probably the wrong job for that.
[0]: https://news.ycombinator.com/item?id=36897054 (my description of my use of multiprocessing) [1]: https://devops-pipeline.com/ (my use of multithreading)
With PEP703 you would compile Python either for multi or single-threading mode. The mode affects the ABI and therefore which C extensions are available. Eventually all C extensions would have an available port to the new ABI.
The chosen solution is similar to how PHP used TSRMLS_ macros in the Zend engine - if threadsafety (ZTS) was #defined, all functions took an extra thread context parameter, breaking ABI.
The chosen solution is similar to how PHP used TSRMLS_ macros in the Zend engine - if threadsafety (ZTS) was #defined, all functions took an extra thread context parameter, breaking ABI.
I'm looking forward to a GIL-less Python. I think the SC's pragmatic approach is the right one.
I do most of my performance coding in Numba which comes with a nogil mode. Still, I have been looking forward to this. The fewer layers we can have in our libraries, the better!
Why would you even want a no-GIL Python? Java and C showed how much more effort it takes to maintain slower thread safe code for no real benefit. Parallelize at the fork level or at the isolated numeric library level.
I’m not in love with some of the details.
PYTHONGIL is an awkward tri-state. 0, 1, and unset all do different things. Wouldn’t some self-explanatory strings be better? PYTHONGIL=auto for the default, force-gil and force-nogil for the forced modes.
PYTHONGIL is an awkward tri-state. 0, 1, and unset all do different things. Wouldn’t some self-explanatory strings be better? PYTHONGIL=auto for the default, force-gil and force-nogil for the forced modes.
Unpopular opinion: This is a missed opportunity.
What?
Python could have been the one language with a sane multithreading model. Now it risks becoming a second version of Java. I fear this will make it a less attractive programming language, not least because it might lose its beginner friendlyness. For example, without the GIL a lot more care must be put into designing your programs. This can be true even though your own code is single threaded, for example when you use a library that is multithreaded and has callbacks to your code
Why?
Free threading as introduced by PEP 703 is well known to be errorprone, hard to get right and generally advised against, unless you know exactly what you are doing. In other words free threading is for expert (as in very experienced) use only. And Python already has an expert mode - called Cython oer Numba to name just two.
Personally I can see no good will come from bringing free threading to the masses. Yes it addresses a common critique (by many) and need (by very few), but it addresses it in a very risky way (for the vast majority of Python users).
A better alternative
IMHO the far better and still my preferred approach would have been to favor a per-thread GIL with an explicit mode to share particular objects. This would benefit everyone without the risks. It would be consistenly beginner friendly, and above all, offer a safe path to concurrent programming without impacting the whole ecosystem. Heck we could even call it the "Pythonic Threading Model", and it would be seen as a differentiator.
What?
Python could have been the one language with a sane multithreading model. Now it risks becoming a second version of Java. I fear this will make it a less attractive programming language, not least because it might lose its beginner friendlyness. For example, without the GIL a lot more care must be put into designing your programs. This can be true even though your own code is single threaded, for example when you use a library that is multithreaded and has callbacks to your code
Why?
Free threading as introduced by PEP 703 is well known to be errorprone, hard to get right and generally advised against, unless you know exactly what you are doing. In other words free threading is for expert (as in very experienced) use only. And Python already has an expert mode - called Cython oer Numba to name just two.
Personally I can see no good will come from bringing free threading to the masses. Yes it addresses a common critique (by many) and need (by very few), but it addresses it in a very risky way (for the vast majority of Python users).
A better alternative
IMHO the far better and still my preferred approach would have been to favor a per-thread GIL with an explicit mode to share particular objects. This would benefit everyone without the risks. It would be consistenly beginner friendly, and above all, offer a safe path to concurrent programming without impacting the whole ecosystem. Heck we could even call it the "Pythonic Threading Model", and it would be seen as a differentiator.
If I remember Guido van Rossum did mention the status of the GIL in one of the Lex Friedman episodes [1] he was on (it's been a while, so I may be misremembering). Surprised to see a big decision like this happen so quickly. Did Meta's announcement play a big role in this [2]?
[1]: https://www.youtube.com/watch?v=-DVyjdw4t9I
[2]: https://news.ycombinator.com/item?id=36643670
[1]: https://www.youtube.com/watch?v=-DVyjdw4t9I
[2]: https://news.ycombinator.com/item?id=36643670
This seems somewhat delayed, and it may be considered too little, too late. Python community had the chance to leapfrog and embrace alternative concurrency abstractions, such as go routines etc, but it appears that this opportunity was not fully utilized.
After enduring the arduous process of migrating from Python 2 -> 3 and navigating the complex world of dependencies, my hope is that we won't encounter another nightmare of dependency management, forcing users to choose between GIL and no-GIL builds.
After enduring the arduous process of migrating from Python 2 -> 3 and navigating the complex world of dependencies, my hope is that we won't encounter another nightmare of dependency management, forcing users to choose between GIL and no-GIL builds.
I sure hope they decide to call execution of mixed GIL and no-GIL codebases "amphibious mode"
Is it really too late to not do this ? The only reason to get rid of the GIL is to help threading, but that's not a thing we should be doing. Threads need to just die, and be replaced by something less idiotic. Seriously, having the CPU run fragments of your program at random, so that all the previously ordered pieces are now contending with each other and even themselves ? How can anyone not see that this is the stupidest idea in the world ?
I wonder if there will ever be Python 4, it seems that the core developers want to avoid bumping the major version number ever again after 3 under any circumstances.
Let us wait and watch but I somehow feel that this no-GIL mode is just a band-aid solution to Python's performance problem. The cause goes deep inside the core of Python, it gradually came to this stage as more and more features got added to the language since the 3.x transition.
I think new language features shouldn't just be added to provide syntactic sugars or coding shortcuts to programmers or just because a certain feature has become very cool (like lambda functions, for eg).
I'm glad that the Python community has realized that performance is an issue and started working on things like no-GIL mode.
People often say that Python's biggest strength is its readability and easy syntax but I disagree. Python's real strength is the enormous third party library ecosystem, popular packages like numpy, pandas, scikit, etc. which have almost become addictive in most data science projects. But now, people are thinking of other alternatives to these due to Python's performance issues. Other ecosystems like golang and rust are getting built at rapid pace and at some point, they will also have (more performant) equivalents of these packages if public shows enough interest.
I think new language features shouldn't just be added to provide syntactic sugars or coding shortcuts to programmers or just because a certain feature has become very cool (like lambda functions, for eg).
I'm glad that the Python community has realized that performance is an issue and started working on things like no-GIL mode.
People often say that Python's biggest strength is its readability and easy syntax but I disagree. Python's real strength is the enormous third party library ecosystem, popular packages like numpy, pandas, scikit, etc. which have almost become addictive in most data science projects. But now, people are thinking of other alternatives to these due to Python's performance issues. Other ecosystems like golang and rust are getting built at rapid pace and at some point, they will also have (more performant) equivalents of these packages if public shows enough interest.
The creator and lead maintainer of SQLAlchemy, one of the most popular and most used Python libraries for accessing databases (who doesn't?) gave a rather interesting response to PEP703.
> Basically for the moment the GIL-less idea would likely be burdensome for us and the fact that it's only an "option" seems to strongly imply major compatibility issues that we would not prefer.
(...)
> Adding an entirely new mode of operation to cPython that's optional would be an enormous burden for us as far as ensuring we use APIs appropriately, adding support, testing, we would have to spin up new test workers to test SQLAlchemy in both modes of operation, we would be getting strange new race condition related issues reported
https://github.com/sqlalchemy/sqlalchemy/discussions/10002#d...
> Basically for the moment the GIL-less idea would likely be burdensome for us and the fact that it's only an "option" seems to strongly imply major compatibility issues that we would not prefer.
(...)
> Adding an entirely new mode of operation to cPython that's optional would be an enormous burden for us as far as ensuring we use APIs appropriately, adding support, testing, we would have to spin up new test workers to test SQLAlchemy in both modes of operation, we would be getting strange new race condition related issues reported
https://github.com/sqlalchemy/sqlalchemy/discussions/10002#d...
It will be interesting to see how this will be executed. I think many in Ruby land wanted something similar but couldn't get some general agreement. Ractors tried and arguably failed. We have Samuel Williams basically the one person pushing very hard for Async changes.
Ruby could learn a lot once this is done, but at the moment GIL optional in python seems to be a 2030 goal.
Ruby could learn a lot once this is done, but at the moment GIL optional in python seems to be a 2030 goal.
How will this impact single thread performance?
I'm looking forward to this, Python plays a fairly significant role in our scientific computing code and not having to have entirely separate processes will be very convenient for cutting down data duplication.
Is the following possible?
- library author marks their library "no-GIL" after making sure it's thread-safe without GIL
- if the interpreter sees this metainformation, it temporarily disables GIL for the current OS thread while running the library's code
- result: old versions of Python can still run no-GIL libraries under GIL, while new versions of Python allow to gradually remove GIL
Or it's not how CPython works?
- library author marks their library "no-GIL" after making sure it's thread-safe without GIL
- if the interpreter sees this metainformation, it temporarily disables GIL for the current OS thread while running the library's code
- result: old versions of Python can still run no-GIL libraries under GIL, while new versions of Python allow to gradually remove GIL
Or it's not how CPython works?
there's a lot of code I wrote (and saw people write) in Python over the years, conscious that noone will ever run it in threads (ofc it's possible, but typically pointless), thus going quite easy on things that wouldn't be thread-safe. this used to quite a comfortable stance. community ended up inventing other ways to share state, other ways to vectorize, other ways to avoid blocking on I/O, that might sometimes be annoying, but evolved to be quite reasonable for Python.
giving up this stance? a lot of code is instantly a legacy, and a lot of it is a legacy people won't even know about before they notice the problems. and for what?
i must say that i have no experience running Python without GIL so my idea of the ways things can be not thread-safe is purely speculative/borrowed from very different languages (that I finally moved on to long ago, thank god). so maybe i'm wrong, i misunderstand the impact, and all this code is just fine
giving up this stance? a lot of code is instantly a legacy, and a lot of it is a legacy people won't even know about before they notice the problems. and for what?
i must say that i have no experience running Python without GIL so my idea of the ways things can be not thread-safe is purely speculative/borrowed from very different languages (that I finally moved on to long ago, thank god). so maybe i'm wrong, i misunderstand the impact, and all this code is just fine
I hope this won't make Python's dependency hell even worse, but I'm not hopeful.
No-GIL means better performance at the cost of being less beginner-friendly, right?
String parsing which tokenised in-place. DNS calls which used static buffers. Things which exploited Vax specific stack behaviour.
I think the GIL has been a blessing and a curse.