# Python 3.13 without GIL: Real-World Threading Finally Works
> Tested Python 3.13's optional GIL. Shared results showing real multithreading gains, install steps, and when it outperforms multiprocessing.

Canonical: https://blog.abhimanyu-saharan.com/posts/making-the-gil-optional-a-deep-dive-into-pep-703
Published: 2025-05-23
Last updated: 2025-05-24
Authors: Abhimanyu Saharan
Categories: Python 3.13, Python

Python 3.13 introduces the most significant concurrency improvement in CPython's history: the ability to **disable the Global Interpreter Lock (GIL)**. Thanks to [PEP 703](https://peps.python.org/pep-0703/), Python can now execute native threads in parallel — not just in theory, but in practice.

This post shares hands-on results after compiling Python 3.13 with `--disable-gil`, running CPU-heavy multithreading tasks, and toggling the GIL on and off at runtime. The verdict? Python threads finally scale — and in some cases, outperform multiprocessing — with no code changes.

## How to Install Python 3.13 Without the GIL

To run GIL-free Python, you’ll need to compile it from source.

### Requirements (Ubuntu)

```sh
sudo apt update
sudo apt install -y build-essential zlib1g-dev
```

### Build and Install

```sh
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz
tar -xf Python-3.13.0.tgz
cd Python-3.13.0

./configure --disable-gil --prefix=$HOME/python3.13
make
make install
```

Add to your shell path or run directly:

```sh
$HOME/python3.13/bin/python3.13 -X gil=0
```

## Benchmarking: Threads vs Processes vs Serial Execution

To truly evaluate the GIL, I wrote the following script to simulate a real CPU workload: calculating factorials using threads, processes, and single-threaded code.

File: script1.py

```python
import sys
import sysconfig
import math
import time
from threading import Thread
from multiprocessing import Process

def time_taken(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"Function '{func.__name__}' took {end - start:.4f} seconds to execute.")
        return result
    return wrapper

def compute_intensive_task(num):
    return math.factorial(num)

@time_taken
def single_threaded_task(nums):
    for num in nums:
        compute_intensive_task(num)

@time_taken
def multi_threaded_task(nums):
    threads = []
    for num in nums:
        t = Thread(target=compute_intensive_task, args=(num,))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

@time_taken
def multi_processing_task(nums):
    processes = []
    for num in nums:
        p = Process(target=compute_intensive_task, args=(num,))
        processes.append(p)
        p.start()
    for p in processes:
        p.join()

def main():
    print(f"Python Version: {sys.version}")
    gil_enabled = sys._is_gil_enabled()
    print("GIL is currently", "active" if gil_enabled else "disabled")

    nums = [300001] * 6

    single_threaded_task(nums)
    multi_threaded_task(nums)
    multi_processing_task(nums)

if __name__ == "__main__":
    main()
```

## Results: GIL OFF vs ON

### GIL Disabled

```sh
PYTHON_GIL=0 ./python3.13/bin/python3 script1.py
Python Version: 3.13.0 experimental free-threading build
GIL is currently disabled
Function 'single_threaded_task' took 4.8051 seconds to execute.
Function 'multi_threaded_task' took 2.3236 seconds to execute.
Function 'multi_processing_task' took 2.6521 seconds to execute.
```

### GIL Enabled

```sh
PYTHON_GIL=1 ./python3.13/bin/python3 script1.py
Python Version: 3.13.0 experimental free-threading build
GIL is currently active
Function 'single_threaded_task' took 4.8303 seconds to execute.
Function 'multi_threaded_task' took 4.5740 seconds to execute.
Function 'multi_processing_task' took 2.5155 seconds to execute.
```

### Interpretation

```html
<table>
  <thead>
    <tr>
      <th>Mode</th>
      <th>GIL Enabled</th>
      <th>GIL Disabled</th>
      <th>Performance Impact</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Single-threaded</td>
      <td>4.83s</td>
      <td>4.80s</td>
      <td>~identical</td>
    </tr>
    <tr>
      <td>Multi-threaded</td>
      <td>4.57s</td>
      <td>2.32s</td>
      <td><strong>&gt;2× faster without GIL</strong></td>
    </tr>
    <tr>
      <td>Multi-processing</td>
      <td>2.51s</td>
      <td>2.65s</td>
      <td>Comparable</td>
    </tr>
  </tbody>
</table>

```

### Observations:

- Without the GIL, Python threads **scale across cores** — no workarounds, no hacks.
- GIL-free threading **outperformed multiprocessing**, without the complexity of IPC.
- The single-thread baseline remains unaffected — the GIL toggle doesn’t impact serial code paths.

## Why This Matters

Historically, Python's GIL has made multithreading useless for CPU-bound code. This forced developers to:

- Use `multiprocessing`, which incurs overhead
- Offload to C or Cython
- Rewrite in other languages (Go, Rust, C++)

With Python 3.13’s `--disable-gil` build, those workarounds are no longer necessary. **Multithreaded CPU-bound Python code is finally viable.**

## Considerations

- GIL-disabled builds are not ABI compatible — C extensions must be rebuilt.
- Some overhead exists in managing locks around containers, especially in pure Python workloads.
- This is still marked as **experimental** in Python 3.13. Future versions will likely optimize further.

## Final Thoughts

Python 3.13 with `--disable-gil` is not just a technical milestone — it's a practical, user-facing shift in what Python can do. For the first time, multithreaded Python isn't a theoretical dream. It's fast, predictable, and works without changing your code.

If your workloads involve concurrency, especially CPU-heavy tasks, this feature is worth adopting early.

## FAQ

### What is the significance of Python 3.13’s --disable-gil feature?

Python 3.13 introduces a GIL-free build option via `--disable-gil`, allowing **native threads to execute in true parallel**. This eliminates the long-standing Global Interpreter Lock bottleneck for CPU-bound multithreaded workloads.

### How does performance compare with and without the GIL?

When the GIL is disabled, **multithreaded CPU-bound code scales across cores**, outperforming both multiprocessing (due to lower overhead) and traditional GIL-constrained threads. Serial code performance remains unaffected.

### Do I need to change my code to benefit from GIL-free Python?

No. Python 3.13’s GIL-free build works **without any code changes**. Existing multithreaded programs can benefit immediately if the GIL is disabled at compile time and runtime.

### Are there any compatibility concerns with --disable-gil?

Yes. GIL-disabled builds are **not ABI-compatible** with standard CPython builds. C extensions must be **recompiled**, and some thread-safety assumptions in third-party packages may need to be reviewed.

### Is Python 3.13’s GIL-free mode production-ready?

It is currently **experimental**. While real-world performance improvements are promising, users should expect ongoing changes and optimizations in future versions before relying on it in critical production environments.
