In a recent post on the Python Foundation blog, developer Alex Waygood touched on a major Python language issue from the Python Language Summit just last week - the “deprecation” of the Python language’s Global Interpreter Lock (GIL).
“Double-Edged Sword”: CPython - Interpreter and Compiler
The flexibility of Python’s dynamic language is known to be a “double-edged sword”. This means that there can be different runtimes, such as Pyston, Cinder, MicroPython, pypypy, etc., which may support the entire language, specific versions, or subsets. But if you’re using Python, then you’re probably running CPython.
CPython is the standard Python interpreter written in C. It also acts as a compiler, since its task is to compile Python code in bytecode form before the actual interpretation phase.
CPython has something called Global Interpreter Lock (GIL) that affects threaded code, i.e. only one thread can be running in the interpreter at a time. As a result, GIL has been seen as an inherent limitation in the development of the language.
Previously, there have also been proposals to address this issue, such as moving performance-critical parts to C or using multiple interpreters. However, to meet these expectations, the audience of interpreter users may expand. There are several alternatives, such as through dedicated JVM (Java Virtual Machine) and CLR (Common Language Runtime) solutions, but most of the above existing solutions have considerable drawbacks.
So with this background, support for “Python without global interpreter locks” is gaining traction.
Repealed Many Times: How to Get Rid of GIL
At the Python Language Summit, Meta’s Senior Director of Engineering Sam Gross raised the topic of “deprecating GIL” as part of the “nogil” project.
The proposal is said to be based on the idea of deprecating GIL in Python, where Gross initially ran into problems with Python projects using third-party code and started thinking about how to make threads safe “without GIL”.
As mentioned earlier, the global interpreter lock GIL can only run one thread at a time in the interpreter, so it might be easier to reason about program state when you can guarantee that only one thread is running at a time. Without GIL, however, reference counting, memory allocation, method resolution sequential caching, and garbage collection threads become unsafe.
So, how do you get rid of GIL?
Sam Gross reportedly devoted an earlier discussion to this evolution. Since CPython is designed to be “thread-safe” but relies on GIL, getting rid of GIL requires, first of all, a major change in reference counting.
In order to know if the garbage collector can release an object in memory, it counts all references to the object. Currently, reference counting is non-atomic, and changing all reference counting operations to atomic operations would have a huge performance impact.
Sam Gross uses a technique called “biased reference counting” in this proposal to obtain local and shared references. Local references can take advantage of non-atomic operations, and owning threads combine local and shared references to keep track of ownership. This approach is ideal for single-threaded objects, or objects that are only used in small numbers by a few threads.
There are several objects in the program life cycle, such as Insert String, True, False, and None, that can be marked as immortal, thus reducing their reference counting overhead to zero. Objects are marked as “immortal” by using the lowest significant bit in the reference count field. Frequently accessed objects that are not guaranteed to be “immortal” delay the reference count, meaning that the only reference count needed is when the reference is stored on the heap, and a side effect of this change is that the object cannot be recycled immediately because the stack needs to be scanned for any remaining references.
Sam Gross replaced the standard pymalloc memory allocator with mimalloc, an alternative to malloc that provides thread safety and performance. The benefit of this swap is that this allocator allows the runtime to look up GC-tracked objects without an explicit list. This is a significant performance gain, but it means that you cannot just swap another allocator that is compatible with malloc and expect garbage collection and collection to have the same thread safety.
Python has not yet decided whether to remove GIL
On the question of why GIL should be removed, the Python Foundation blog explains, “In order for Python to work efficiently without GIL, new locks must be added to most code to ensure that it remains thread-safe, but adding new locks to existing code can be very difficult because of the potential for significant slowdowns in some areas. "
This time, Sam Gross’ new proposal to “remove the GIL” seems to have been “enthusiastically” welcomed by the rest of the Python core development team. Now, the main question to be addressed is how to implement such a huge change in CPython.
The next version of CPython (or CPython 3.11) is reportedly expected to be released in October 2022, and it is not known if there will be a major update at that time, but reports suggest that developers are particularly interested in this update for higher performance and integration of support for running in the context of a web browser.
For some time now, developers have tried to deprecate GIL in the standard implementation of CPython because it has hindered the language’s progress. This time, a new proposal to “remove GIL” is finally here, and while Python has not yet made a final decision on implementation, it’s still worth waiting for.