> or is there a fundamental requirement, e.g. C programs unavoidably interact directly with the GIL?
Both C programs can use the GIL for thread safety and can make assumptions about the safety of interacting with a Python object.
Some of those assumptions are not real guarantees from the GIL but in practise are good enough, they would no longer be good enough in a no-GIL world.
> I know that the C-API is only stable between minor releases [0] compiled in the same manner [1], so it's not like the ecosystem is dependent upon it never changing.
There is a limited API tagged as abi3[1] which is unchanging and doesn't require recompiling and any attempt to remove the GIL so far would break that.
> so it's not like the ecosystem is dependent upon it never changing
But the wider C-API does not change much between major versions, it's not like the way you interact with the garbage collector completely changes causing you to rethink how you have to write concurrency. This allows the many projects which use Python's C-API to relatively quickly update to new major versions of Python.
> I have found a no-GIL interpreter that works with numpy, scikit, etc. [2][3] so it doesn't seem to be a hard limit.
The version of nogil Python you are linking is the product of years of work by an expert funded to work full time on this by Meta, the knowledge is sourcing many previous attempts to remove the GIL including the "gilectomy". Also you are linking to the old version based on Python 3.9, there is a new version based on Python 3.12[2]
This strays away from the points I was making, but with this specific attempt to remove the GIL if it is adopted it is unlikely to be switched over in a "big bang", e.g. Python 3.13 followed by Python 4.0 with no backwards compatibility on C extensions. The Python community does not want to repeat the mistakes of the Python 2 to 3 transition.
So far more likely is to try and find a way to have a bridge version that supports both styles of extensions. There is a lot of complexity in this though, including how to mark these in packaging, how to resolve dependencies between packages which do or do not support nogil, etc.
And even this attempt to remove the GIL is likely to make things slower in some applications, both in terms of real-world performance as some benchmarks such as MyPy show a nearly 50% slowdown and there may be even worse edge cases not discovered yet, and in terms of lost development as the Faster CPython project will unlikely be able to land a JIT in 3.13 or 3.14 as they plan right now.
>> So basically a JIT would help every Python program, and removing the GIL would only help a small subset of Python programs.
What if the "Global Interpreter Lock" needs to be removed for JIT? I put that in quotes to highlight it because AFAICT no compiled (or JITed) language has such a thing. I think it functions differently than regular stuff like critical sections.
> If it's such a huge game changer why don't some of the large enterprises which rely on Python fund this work?
Because lots of code in python relies on the GIL to act as a synchronization primitive for threaded code (either explicitly or implicitly) and removing the GIL will break that code in subtle ways and there’s no appetite in those companies to fix that.
> The purpose of this document and associated code is to convince you of a few things:
> That it is feasible to remove the GIL from Python.
> That the tradeoffs are manageable and the effort to remove the GIL is worthwhile.
...
> Removing the GIL will require making trade-offs and taking on substantial risk:
> Removing the GIL will be a large, multi-year undertaking with increased risk for introducing bugs and regressions.
> Removing the GIL means making trade-offs between single and multi-threaded performance. I believe we can make a future CPython without the GIL that is faster for both single and multi-threaded workloads than current CPython, but if we keep the GIL and focus solely on single-threaded workloads, we would achieve even better single-threaded performance. (See the “Performance” section for more details.)
I don't want to be too cynical (I am after all not critiquing the details), but this is basically how every remove the GIL project starts. many intelligent people have tried. Many have failed. There are just so many trade-offs, compatibility issues, etc. I'm not personally against this project, but really I'd rather read a document describing the successful result rather than outlining the goals.
That said work on cpython that tries to clean up internal state has gone on for many years and is great. That sort of thing might make removing the GIL more reasonable in the future, but also makes the code easier to understand and work with in the present.
Regardless of my negativity, I wish the author good luck!
> Nobody has ever chosen Python for its runtime performance.
No, they choose it for the ease of using its performant extensions. And those extensions are fundamentally limited in performance by the existence of the GIL. And the authors of those extensions (and their employers) are behind the work to get rid of it.
There are three groups here:
1. "Pure" Python users, whose code makes little/no use of extensions. GIL removal is an immediate win for these uses.
2. "Good" extension users/authors, whose code will support and benefit from no-GIL. GIL removal is an immediate win for these uses.
3. "Bad" extension users/authors, whose code doesn't/can't support no-GIL. GIL removal probably doesn't break existing code, but makes new uses potentially unsafe.
Maintaining the technical debt of the GIL indefinitely so that group 3 never needs to address its own technical debt is not a good tradeoff for groups 1 and 2 which actively want to move the language forwards.
> Yeah, I know about that argument but it just doesn't make sense to me. Removing the GIL means that 1) you make your language runtime more complex and 2) you make your app more complex.
#2 need not be true; e.g., the approach proposed here is transparent to most Python code and even minimized impact on C extensions, still exposing the same GIL hook functions which C code would use in the same circumstances, though it has slightly different effect.
> I would argue the GIL is a feature put in place to make it easier to write parallel code.
It doesn't do that though. The GIL protects interpreter internals. It doesn't make Python code thread-safe, although it does make various operations atomic:
* native calls (which doesn't release the GIL)
* individual bytecode instructions (which don't call into more python code)
The latter can make it seem like it magically protects your code, but even a simple method call is at least 3 instructions (LOAD_FAST, LOAD_METHOD and CALL_METHOD). Each instruction is atomic and the method call will be if it's a native call (e.g. dict.get) but that's it, the entire call itself is not atomic and you could thread-switch between LOAD_FAST and LOAD_METHOD, or LOAD_METHOD and CALL_METHOD.
Every single HN thread on python performance, ever:
Person with limited to zero experience with CPython internals> I hate the GIL, why don't they just remove it?
That just is doing an incredible amount of heavy lifting. It'd be like saying, "Why doesn't the USA just switch entirely to the metric system?" It's a huge ask, and after being burned by the 2/3 transition, the python team is loathe to rock to boat too much again.
The GIL is deep, arguably one of the deepest abstractions in the codebase, up there with PyObject itself. Think about having to redefine the String implementation of a codebase in your language of choice.
Whatever your monday-morning quarterback idea on how to pull out the GIL is, I can almost guarantee, someone has thought about it, probably implemented it, and determined it will have one or more side effects:
- Reduced single-thread performance
- Breaking ABI/FFI compatibility
- Not breaking ABI immediately, but massively introducing the risk of hard to track concurrency bugs that didn't exist before, because GIL
- Creating a maintenance nightmare by adding tons of complexity, switches, conditionals, etc to the codebase
The team has already decided those tradeoffs are not really justifiable.
The GILectomy project has probably come the closest, but it impacts single-thread performance by introducing a mutex (there are some reference type tricks [1] to mitigate the hit, but it still hurts run time 10-50%), and necessitates any extension libraries update their code in a strongly API-breaking way.
It's possible that over time, there are improvements which simultaneously improve performance or maintainability, while also lessening the pain of a future GILectomy, e.g. anything which reduces the reliance on checking reference counts.
[1] PEP 683 is probably a prerequisite for any future GILectomy attempts, which looks like it has been accepted, which is great https://peps.python.org/pep-0683/
> I am advocating a GIL per-thread model with an explicit feature for sharing selected objects
Is there a PEP or something written about it? I don't like this idea at first glance. Feels like another hack. Though I have to admit it sounds better than the annoying multiprocessing approach.
> It's not optional if you build tools and libraries that need be able to run with both gil and no-gil.
Why do they need to? A library dev is free not support one or the other? If a library then does not provide the preferred mode of the user, then that's bad, but that's life. Ideally most libraries will support a gil version (that's still the default anyway!), but provide a no-gil version as a bonus. For example, Pytorch could provide thread-based data loaders if the no-gil version is in use.
> GIL in python is something that was talked about 5 years ago and will still be talked in 5 years. It limits the ability of python to reap the benefits of better hardware.
It's also a huge source of misattributed problems — I've seen more cases where a complaint about the GIL was really “my algorithm depended on a single locked data structure” or “I was calling a C library which isn't thread-safe” than where the GIL was actually the limiting factor. That's not to say that there aren't real problems for people who want to run pure-Python computational code (not e.g. libraries like Numpy or Pillow) but it also seems to be popular as the bogeyman to blame when someone doesn't want to run a profiler.
> This may be minor, but I really missing ruby rich set of collection methods: take_while, group_by, sample. Yet I can see a point in extracting that to a external library.
> The big problem with the 2->3 transition was that the cost exceeded the perceived benefit.
I believe it's generally agreed that the cost of the transition was surprisingly high. Hindsight is 20/20.
> Removing the GIL would have increased the cost, but also the benefit.
The only attempts I'm aware of resulted in a significant (~50%) slowdown in single-threaded code execution due to the requirement of adding in so many more locks elsewhere (removing the GIL doesn't remove the need for the locks around the various critical sections). Sure maybe it would result in better performance for some programs, but I'm not even sure it would result in something better than a cleaner design. In my experience, the portions of code that could make use of the threading could just be moved to a C/C++-extension and make use of it there (though in 99% of cases I wouldn't go that far and just stick to e.g. numpy). The examples of the code that really should stay in python, but also should make use of concurrency seem to usually sit quite nicely in the pypy framework.
That's not to say that removing the GIL wouldn't be nice, but I think the need for it is often overstated.
> The idea is that it's an investment to be recouped for various workloads due to the improved ability to exploit concurrency over a GIL-ful interpreter.
This idea has been rejected by the core team though, getting rid of the gil with a major performance hit is not considered acceptable when most existing Python code is not significantly affected by the GIL.
> The GIL will never be removed from the main python implementation.
I don't see why. It's a much easier transition than 2 to 3.
Make each package declare right at the top if they are non-GIL compatible. Have both modes available in the interpreter. If every piece of code imported has the declaration on top, then it runs in non-GIL mode, otherwise it runs in classic GIL mode.
At first most code would still run in GIL mode, but over time most packages would be converted, especially if people stopped using packages that weren't compatible.
> The GIL is actually a way of keeping Python internally uncomplicated at the price of lost efficiency.
Lost efficiency? Barring significant engineering effort, the GIL is definitely more efficient than more fine-grained locking in a single-threaded situation. Which the Python core team considers of prime importance and the main use-case for Python (whether you agree or not is a different issue): previous tentatives to break-up the GIL lead to performance hits considered unacceptable (~20% across the board I believe).
It also tends to keep Python code less complicated as — much as with Javascript — you can not have two threads of Python code running at the exact same time within a process (this, on the other hand, is a very mixed blessing: it prevents people from learning about the brokenness of thread-based concurrency, and it generally breaks code migrated on a GIL-less implementation such as Jython or IronPython)
> But the reason I say it's deep is because of the deep implications and assumptions of the GIL being there. For decades, the codebase (and extensions) has grown with the expectation that the GIL enforces a certain behavior.
As i mentioned... The idea of keeping backward compact. of an implementation details which leaked into the API is kinda common. Some of the current proposal are about making the gil optional, keeping the compact for those cases...
> Precisely. Because it's such a hard problem. If someone had a proposal to actually remove the GIL, limit the single core performance hit to say <10%, and not break extensions, I have no doubt the steering team would go for it. It would be huge PR for python, simply because of the Gil's bad rap.
I think this might be were we disagree the most. One doesn't sit around, put a collection of constraints into the ether and expect a solution to magically appear... Obviously every one wants a perfect solution to technical problems, but those don't really exists.
The true measure of the willingness of the council to accept gil is about the compromises they are willing to make to accept external solutions or the effort put into developing their own solutions.
Even if the council just had put some effort into developing a suits of test cases (both performance and correctness), or try to pull some statistical information on how many C extensions rely on the gil for correct behavior, how hard would a transition for those be? can we leverage runtime tooling like valgrind and/or llvm (thread|memory)sanitizer to detect some and ease the transition.
But i haven't seen any real effort, just the same list of constraints we had the last 10 years.
> Turns out that multi-interpreters give you most of the benefit of multithreading with nowhere near as much complexity.
I am only tangentially familiar with the sub interpreter proposal, but what i understand, i disagree. This the classic sharing memory vs message passing trade-off...
Also not to forget that this does might still break C-extension which sometime relies on static variable (like the comment from the author or mysql extension mentioned).
But i don't want to make this about this or that specific proposal. if the council prefer sub interpreter approach to nogil, they should say so and don't have people waste their time.
> What other dynamic languages invented over 3 decade ago have natively multithreaded interpreters? Oh javascript, the (fundamentally simpler on a technical level and uses different gc techniques) language which has gotten millions of dollars in development by the biggest tech companies because it runs on nearly every browser and thus has huge incentives to optimize? If python ran in every browser, the Gil would be gone by now.
> Java? Again, huge amount of backing, 15 years older than python.
> Perl, ruby, lua, all use refcount and have single-threaded interpreters. Objective C might fit the bill for multithreaded refcount gc (I don't know, I'm asking gpt). Nim does, but it's rather modern, (2008). Swift is statically typed, which helps, and much newer (2014).
I don't want to sound to harsh, but you go to war with the army you have, not the army you want. The question is not what the optimal solution would look like if we had optimal conditions (deeper pockets, gc vs refc, etc... etc...).
Python is what python is, the python organization have the resources that they have. No doubt we could design a better solution with more money or a with a language with better semantic, but don't have neither and that shouldn't prevent the council of acting on the best solution we have right now...
There is a lot of work going on with sub-interpreters and the per-interpreter GIL is shipped in Python 3.12.
The results are very impressive: https://lwn.net/SubscriberLink/941090/8bcb029dbf548f26/, as good as one could have hoped I think.
It seems to me like the work on sub-interpreters will continue in parallel ;) to the work on free-threading.
Sub-interpreters and no-GIL have different use-cases though.
reply