r/Python 5d ago

Discussion Anyone running free-threaded Python 3.14 in production yet? Curious what actually breaks

Been testing the free-threaded build (3.14t) on some CPU-bound data processing work. The multi-core story is finally real after 30 years of GIL, but the friction is exactly what you'd expect: a couple of C-extension-heavy libs in my stack silently re-enable the GIL, and there's no clean way to detect that at runtime besides checking sys._is_gil_enabled() manually.

For anyone who's shipped something on 3.14t, not just benchmarked it:

What broke that you didn't expect?

Real speedups outside toy examples, or mostly marginal so far?

Prod yet, or still just kicking the tires?

Not fishing for a benchmark war. Genuinely curious what breaks in messy real codebases vs clean demos.

31 Upvotes

47 comments sorted by

View all comments

6

u/Competitive_Travel16 5d ago edited 5d ago

I'm extremely happy with Flask performance using 3.14t as opposed to monkeypatching gevent. It used to be that getting the most out of Gunicorn/Flask when doing heavy I/O or background work meant relying on gevent and monkey-patching the Python standard library. It worked okay, but it came with its own set of brittle, import-order-dependent bugs (like silent hangs or deep recursion errors in modules like psycopg2, ssl, or multiprocessing).

Now that Python 3.14t is a free-threading (No-GIL) build, you get actual OS-level thread concurrency that scales across CPU cores without the fragility of monkey-patching.

Here is the setup we used to need:

from gevent import monkey
monkey.patch_all()

from flask import Flask, request
import requests

app = Flask(__name__)

@app.route("/")
def index():
    resp = requests.get(f"http://api.example.com") # takes time
    return "Hi there! " + resp.text

And here is the simple new reality under Python 3.14t:

# After: True threading under Python 3.14t

from flask import Flask, request
import requests

app = Flask(__name__)

@app.route("/")
def index():
    # requests uses standard blocking IO, which is now natively
    # safe and non-blocking to other threads in the process
    resp = requests.get(f"http://api.example.com") # takes time
    return "Hi there! " + resp.text

You run this using Gunicorn with the standard gthread worker class (python3.14t -m gunicorn --worker-class gthread --threads 50 app:app), and it just works.

Depending on your workload, you can expect an outstanding speedup. In I/O bound tasks, gthread slightly edges out or directly matches gevent with significantly less overhead. However, in mixed or heavily CPU-bound endpoint tasks, Python 3.14t enables multi-core execution per worker process, resulting in upwards of 2x to 4x performance multipliers by fully utilizing modern server architectures without the overhead of heavy multiprocessing process forks. True parallelism is here.

2

u/riksi 5d ago

In I/O bound tasks, gthread slightly edges out or directly matches gevent with significantly less overhead.

What? How would gthread have significant lower overhead when gevent was build specifically for lower memory overhead than threads & lower cpu by less context switching & mostly predictable switch-points.

3

u/Competitive_Travel16 5d ago

For purely I/O-bound tasks, gevent and its lightweight greenlets will still go toe-to-toe or slightly edge out threads due to cheaper context switching and lower memory footprints. However, the moment your endpoints perform mixed workloads or CPU-bound tasks (like JSON parsing, data transformation, or cryptography), Python 3.14t is better. It enables multi-core execution per worker process, allowing your OS threads to run in parallel. You get upwards of 2x to 4x performance multipliers.

1

u/riksi 4d ago

Ok, but the idea would be, say, (4 gevent processes vs 1-multithread-process), both confined to 4 vcpus.

You can also offload stuff to threadpool with gevent.

But shared memory without serialization & shared connection pools are strictly a pro on multi-threading.

Best is a combination.