11.1 Global Interpreter Lock
Global Interpreter Lock (GIL)
is a mechanism in the
CPython interpreter that ensures only one thread executes Python bytecode
at a time. GIL
prevents threads from running in parallel,
which can negatively impact the performance of multithreaded programs,
especially on multicore processors.
Reasons for GIL
existence
Simplifies memory management: GIL
simplifies
memory management and garbage collection, making Python easier
to implement.
Thread safety: GIL
prevents race conditions,
making the code execution thread-safe without the need
to use locks.
Issues caused by GIL
Limited performance: Multithreaded
programs performing computationally intensive tasks cannot fully
exploit the benefits of multicore processors due to the
limitations of GIL
.
Performance distortion: Programs that heavily use threads to perform tasks may experience degraded performance due to context switching between threads.
Currently, there are 4 main ways to "bypass GIL":
11.2 Using Multiprocessing (multiprocessing)
The multiprocessing
module allows you to create
processes that run in parallel and are not limited by GIL
, since
each process has its own Python interpreter and memory.
Example:
import multiprocessing
def worker(num):
print(f'Worker: {num}')
def main():
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
main()
11.3 Asynchronous Programming
Asynchronous programming using
asyncio
allows tasks to run concurrently without
blocking the main thread. While it doesn't bypass GIL
in the direct sense,
it enables efficient use of wait times for performing
other tasks.
Example:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 Using Libraries with Native Threading
Some libraries, like NumPy and SciPy, are written in C and use their own threading mechanisms, which allows them to bypass GIL and efficiently use multicore processors for computations.
Really, this is one of the main reasons for Python's success, no matter how slow it is. All complex calculations are rewritten in C/C++ and executed across all CPU cores or even directly on GPU cores. Modern GPUs have thousands of cores.
So, it doesn't matter anymore how fast or slow a language is, if all resource-intensive computations are done by external libraries or are offloaded to remote data centers.
11.5 Performing Computations Outside the Python Interpreter
Using C/C++ extensions or other languages that can perform computations outside the Python interpreter and then return the result. This avoids GIL blocking during intensive computations.
Example using Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Compilation and usage:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION