why python is terrible

Workspace actions
Current node Answer

Dismantling the Engine: Inside the CPython GIL Source Code

If the pursuit of a concurrent Python unicorn is a form of software alchemy, then the Global Interpreter Lock (GIL) is the physical law that dooms the transmutation. To truly understand why Python cannot escape its single-threaded heritage, we must move beyond abstract design philosophy and look directly at the C source code of CPython, the reference implementation of the language. ## The Physicality of the Lock: `pycore_ceval.h` The GIL is not a conceptual behavior; it is a concrete mutex defined in the CPython internal headers. In the [CPython GitHub repository](https://github.com/python/cpython), specifically within the internal header [pycore_ceval.h](https://github.com/python/cpython/blob/main/Include/internal/pycore_ceval.h), the GIL is represented structurally within the helper runtime state: ```c struct _gil_runtime_state { unsigned long interval; _Py_atomic_address locked; _Py_atomic_int requests; /* ... Mutexes and condition variables ... */ }; ``` This structure reveals that the GIL relies on an interval-based preemption model. As explored in Antoine Pitrou's seminal [design of the new GIL](https://github.com/python/cpython/blob/main/Python/condptr.h) (introduced in Python 3.2), the lock does not continuously poll. Instead, it uses a system-level condition variable to force the holding thread to release the lock after a set duration—by default, 5000 microseconds (5 milliseconds)—if another thread requests it. ## The Illusion of Release: The Evaluation Loop To see the GIL in action, one must examine the heart of the interpreter: the evaluation loop in [ceval.c](https://github.com/python/cpython/blob/main/Python/ceval.c). Here, the interpreter executes compiled bytecode instructions. Within this loop, CPython periodically checks if another thread has requested the GIL: > "The main loop `_PyEval_EvalFrameDefault` check counter is decremented on each instruction. If it reaches zero, the thread suspends execution, releases the GIL, waits for a signal, and reacquires it." This mechanism introduces a profound paradox: **the act of releasing and reacquiring the GIL can consume more CPU cycles than the actual computational work being performed**. This phenomenon, known as the "convoy effect" or "GIL thrashing," occurs when I/O-bound threads repeatedly wake up and force compute-bound threads to yield, resulting in massive context-switching overhead. As David Beazley demonstrated in his classic research on the [Inside the Python GIL](http://www.dabeaz.com/python/GIL.pdf) presentation, under multi-core environments, this battle for the lock leads to severe performance degradation because the OS thread scheduler and the CPython GIL state machine actively fight each other. ## PEP 703 and the "No-GIL" Surgical Extraction The most radical evolution in Python's history is [PEP 703 -- Making the Global Interpreter Lock Optional](https://peps.python.org/pep-0703/), authored by Sam Gross. To understand the sheer complexity of removing the GIL, one must look at what replaces it in the code: 1. **Biased Reference Counting (BRC)**: Instead of a single atomic increment/decrement for garbage collection, memory allocation must now distinguish between the owning thread of an object and foreign threads, tracking local and shared reference counts separately. 2. **Thread-Safe Allocators**: The default memory allocator (`mimalloc`) must be integrated to handle concurrent allocations without a global bottleneck. 3. **Hazard Pointers**: To safely read dynamic dictionary and list structures without locking them, Python must employ lock-free data structures that utilize hazard pointers to defer memory deallocation. This architectural shift proves that removing the GIL is not a simple deletion of a mutex; it requires a complete rewrite of CPython's memory management. It highlights a stark computer science reality: to achieve true multi-threaded concurrency, we must trade the elegant, single-threaded speed of simple pointer manipulations for highly complex, multi-threaded coordination protocols.

Continue this thread

This path ends here for now.

If you want to keep exploring this line of thought, open the editor and add the next question or answer from this endpoint.

Continue this thread in the editor on desktop.

Other paths you could read

Earlier, at The Velvet Cage of Python, the conversation split. If this is not the thread you want, you can switch to one of the other paths below.

Reading key

Highlights

No highlights yet

Select text to save it here.