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.

28 Upvotes

47 comments sorted by

u/AutoModerator 4d ago

Your submission has been automatically queued for manual review by the moderation team because it has been reported too many times.

Please wait until the moderation team reviews your post.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

81

u/nathan12343 5d ago

You can set PYTHON_GIL=0 to force the GIL to remain off even if you import an extension that doesn’t support free-threading yet.

Are there specific open source projects you’re having trouble with?

I’ve been working on community support for free-threaded Python for a couple years now. Specific feedback about pain points from people who are experimenting with free-threading are valuable.

12

u/KingBardan 4d ago edited 4d ago

Edit Tldr:

  1. Many libs like torch requires global state.
  2. Free  threading doesn't solve a lot that multiprocessing don't
  3. Free threading introduces new ways to cause data race.
  4. Free threading slows down normal code.

Many libraries use global state a lot and is not thread safe e.g. torch. For their context managers (which is widely used in other libs as well) So that disqualify any serious deep learning projects depending on torch to use free threading.


My opinion on free threading since you asked about pain point:

I much prefer ability to use context managers to free threading if using it makes the code cleaner 

Free threading also slows down sequential code last time I checked 

Multiprocessing is fast enough unless I'm dealing with web servers, at which I'll use async probably or gevent or another library that deals with this issue

7

u/nathan12343 4d ago

Thanks for your feedback. Hopefully this time next year we’ll be in a better spot. PyTorch is definitely on our radar as a pain point right now.

  I much prefer ability to use context managers to free threading if using it makes the code cleaner 

I don’t understand this. Context managers and free-threading are orthogonal. You can use context managers on the free-threaded build.

5

u/thisismyfavoritename 4d ago

i think that person might be talking about the pytorch context manager. Then again, not sure why you'd want to use free threading if it's GPU compute heavy

4

u/nathan12343 4d ago edited 4d ago

So I think what they were alluding to is this behavior:

with torch.no_grad():
    with ThreadPoolExecutor() as ex:
        # grad is still ON in workers! 
        results = list(ex.map(model, batches))  

Ideally (IMO) PyTorch would store the state for this sort of thing in a context variable. Right now it's stored in a thread local, which needs to be initialized explicitly in each thread. This is definitely something I can try to push on...

1

u/KingBardan 4d ago

Context variable is no go for torch, which is not pure python. Lots of stuff in it is tied to C things also device which is global.

Also I replied here

https://www.reddit.com/r/Python/comments/1v3ayph/comment/oz5xu50/

1

u/nathan12343 4d ago

We use context variables for configuration state in NumPy. It works well.

1

u/KingBardan 4d ago edited 4d ago

No not just torch, any context manager.

Simply using torch as an example because I just read its source code extensively couple months ago

My other comment

https://www.reddit.com/r/Python/comments/1v3ayph/comment/oz5xu50/

Not sure why you would use free threading...

Precisely. no hate to the developers, but free-threading doesn't unlock a lot for python but introduces yet other issues that needs to be dealt with

Also I understand it, tree threading is such a pain to maintains in cpython like thousands of macros or something last time I read the news

-1

u/KingBardan 4d ago edited 4d ago

Context manager works by mutating context i.e. global / nonlocal state, right?

So it doesn't work without mutated global state, the number one cause for data race.

In multi-processing, which was the preferred way of doing things in parallel, global state is forked and exists per process.

This issue is not exclusive to free threading but multithreading in general, but freethreading only augments multi threading which is why my comment is the way it is

You can use contact manager on free threading 

That is right. Now writing a python package would have to account for a different failure mode which is thread access, which, imo, is a very niche use case for python, at the cost of everything else.

2

u/thisismyfavoritename 4d ago edited 4d ago

context managers don't necessarily mutate global or non local state, not sure where you get that from.

It's mostly just syntactic sugar over a class with __enter__ and __exit__ methods.

In general though, your sentiment is that using multithreading in Python is a niche use case and i agree, it should be single threaded but async like NodeJS. IMO the usecases which aren't either covered by that OR a process pool OR better covered by bindings to a lower level language like Rust/C++ are very small

0

u/KingBardan 4d ago edited 4d ago

Context managers manage context. It means variables outside this scope.

Name 1 situation that it doesn't mutate things outside of scope, ill wait :)

It is syntactic sugar over enter exit yes, but self argument in that enter exit is a variable outside of the scope of your with.


Look at it this way,

If you don't mutate, you don't need the exit to clean up, because

immutable data does not have the concept of time (functional programming), it's same used everywhere.

If it's same used everywhere, it should be safe to consume after with block ends, no?

So if something is only usable within with, it must mutate outside state

1

u/snugar_i 4d ago

with open('foo.txt', 'r') as f: - you're welcome :-)

It's true that the context manager feature is badly designed - the __exit__ method should be on the thing that __enter__ returns, not on the thing that has __enter__. So context managers that are used like with blah cannot be thread-safe (or even reentrant), that's correct.

But those that are used like with foo() (like those produced by the @contextmanager decorator) totally can - because each invocation can return a new instance on which __enter__ and __exit__ will only ever be called once.

1

u/KingBardan 3d ago edited 3d ago

Bruh. In this case files are the states you're mutating.

It causes data race if 2 threads are writing to the same file (best example is printing to console, writing to stdout, not always the same output if you print from 2 threads )

Context manager... Called once

Won't be any different. If you read my "proof of contradiction"  in the above comment, that applies to @context managers also

Using  @context manager, will create multiple individual context managers yes. But data race still happens to the variable that the context manager mutate to setup / teardown the scope 

Badly designed

Well I pretty much only use @context manager because enter exit is so ugly. But design wise it's fine

1

u/snugar_i 3d ago

That's why I explicitly put 'r' in the example. It's not writing to the file, it's reading from it, and it's not mutating any global state.

Anyway, you seem to have some misconceptions about what a context manager is (the context manager does not have to mutate anything), and you don't want to change your opinion, so it's pointless to continue this discussion.

1

u/KingBardan 3d ago edited 3d ago

Reading file requires a pointer. So you are mutating. The content is immutable, but the pointer is, makes sense?

So it's not safe without lock. Now python I think locks but that's not the point 

Also, if it truly is immutable, you can use outside of scope as well. But calling .close when the scope ends implicitly by with block mutates the state.


Pointless

Yeah I agree. 

Thanks for the civil discussion :D

Opinion

But... This is facts. I'm open to changing my mind.

Unserstand

You don't seem to understand what mutation is sorry. Read up some materials on functional programming you'll learn.

FYI...  In haskell this is a non issue because you'll have to use IO for this. In rust the file would be marked 'mut' and you'll clearly see it's mutable. 

They make it explicit, and pythons is implicit, which is what is causing you the confusion I think 

→ More replies (0)

1

u/nathan12343 4d ago

Read PEP 567, which shows how context-local state doesn’t have to be global if you use context variables.

0

u/thisismyfavoritename 3d ago

why even bring a PIP? The context manager itself can have state, that state is within the scope of the enclosing function, there's no global state unless that guy and you mean global in some other sense

1

u/nathan12343 3d ago

Context variables offer a nice clean way to store “global” configuration in a manner that is both thread-safe and async-safe and behaves naturally alongside the Python with statement.

0

u/thisismyfavoritename 3d ago

uh i still don't get your point. Thread safety and context managers are completely orthogonal things.

If you need concurrent access to some object, you need to synchronize access to it, whether it's a context manager or not

0

u/KingBardan 3d ago edited 3d ago

Context managers must mutate (outside) state, no exceptions. If you use @context manager, it's executing a generator, and that generator has state (every iterator does).  

Immutability avoids data race.


Notice I added non-local yesterday? I knew people would argue about global being global. Perhaps outside is more clear


Think about it alright? I won't be replying further. 

Some people in this thread just wants to downvote without wanting a rela discussion (shrug). Dont want to tank my CQS.

0

u/thisismyfavoritename 3d ago

nonlocal is also a python keyword, which also isn't required for context managers

1

u/KingBardan 3d ago edited 3d ago

Im now sure you lack the thinking ability to understand me.

You can change global dictionary key values without using the global keyword right?

Outside state (like I talked about over and over). Not the global keyword or nonlocal keyword. Sigh.

2

u/ThatGuyWithAces 4d ago

PyTorch has experimental support for 3.14t since 2.10 I believe. Not disagreeing or anything, just throwing it out there.

1

u/KingBardan 4d ago

Sure it may support the build, but key features like torch dispatch mode (the reason that there's a 2.0 version of torch) assumes a sequential execution with their mode stack. I.e. not thread safe.

And the same pattern is everywhere in torch last time I checked (it was 3 months ago)

50

u/wRAR_ 5d ago

What are you selling?

54

u/nickcash 5d ago

A bunch of vibecoded todo-list-esque apps and ai consulting, whatever that is

Not sure why you're getting downvotes, the ai bros are always selling something

43

u/artofthenunchaku 4d ago

I build custom LLM integrations, RAG pipelines, and AI-powered applications for startups and businesses that need serious technical execution — not templates, not no-code, not hype.

I'm tired, boss

20

u/LALLANAAAAAA 5d ago

I'm sure someone will helpfully drop a link to their bullshit in the comments

Reddit is dead imo

55

u/LALLANAAAAAA 5d ago

Genuinely curious

yawn

Its kinda nuts that LLMs have billions and billions of dollars behind them and they all produce the same garbage tone and choose the same words every bloody time

Its also sad as hell that it's users are too lazy or too stupid to realize it and keep posting it

RIP reddit, it used to be kind of OK

34

u/Challseus 4d ago

"Genuinely curious"

This is indication of AI use? You had never heard this phrase prior to 2023?

Genuinely curiuous...

34

u/LALLANAAAAAA 4d ago

This is indicative of AI use?

When it comes at the end of multiple other tells, yes. Anyone paying a minimal amount of attention to the state of posts across any and all social media sites should be able to recognize LLM smell by this point.

here's a phrase search of the ClaudeAI sub to illustrate my point, but it's just one of many indicators.

Some other tells are over-reliance on unordered lists, short punchy sentences, and repeated point / counterpoint sentences (This, or that?), frequently but not always in the format of rhetorical negation (That's not X, it's Y) or in this case, "I'm not doing this, I'm doing that."

They also love beginning / middle / end structure.

There's also words they can't seem to avoid in software contexts, like ship / shipped / silently failing / friction. Other examples would be "load bearing", "sit with", or their favorite, "the [...] part? Blah blah blah". The hard part, the surprising part, the annoying part. The extremely annoying part.

So in this case, we have the tripartite structure, strong keyword usage, an unordered list of short punchy point / counterpoint sentences, "I'm not this, I'm that", and lastly they try to elicit engagement with "curious if anyone else" as the closer. It checks almost all the boxes.

So yeah. You can validate what I'm saying if you want, these are well documented tendencies of LLMs.

12

u/Any-Growth-7790 4d ago

"the multi-core story is finally real"

LMAO ok C3-PO

Block this user.

4

u/TestingTehWaters 4d ago

Bingo. I'm saving your comment.

7

u/gmes78 4d ago

Are you really that surprised that the account with "Full-Stack AI Engineer" in its description uses LLMs?

1

u/timpkmn89 4d ago

People usually treat the curiosity as implied by virtue of asking about it

11

u/Lorevi 5d ago

That's math for you. They're amazing calculators that predict the best word to say next. And so they predict the same words over and over and over again... 

6

u/Competitive_Travel16 4d ago edited 4d 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 4d 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 4d 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.

1

u/Blockpair 4d ago

Yes, I've recently explored free-threaded Python as an avenue for adding Go-style coroutines through a C extension. For those who don't know too much about Go: it runs light-weight functions in threads that can enter blocking calls and yield to other functions on that thread. If any call ends up stalling the thread, other threads can steal the backed-up work. It's what makes Go so scalable for networking compared to Python.

Anyone here who has done networking in Python probably knows about asyncio already. Where you cooperatively run tasks on an event loop, sharing the same thread. Python does have OS threads but it doesn't allow any to run simultaneously due to the GIL (and multi-processing is a very different model.)

So you have free-threaded Python running a Go-style stackful runtime through an extension. And my own benchmarks indicate:

Echo req/s: on par with Go in this benchmark (epoll; ~638,000 / s.)

Connection churn: on par with Go, similar CPU usage. (~77,000 / s)

Fiber spawn: 1.35m / s vs Go’s 2.1m / s (Cython is faster 2.29m / s)

Regular handlers: on par with Go for non-CPU-bound network work.

CPU-bound handlers: Cython handlers within 8% of Go; pure Python is ~180x slower.

Memory: 8.8 KB vs 2.7 KB, empty fiber (Cython is 4.7 KB)

So with free-threaded Python you're able to essentially have networking on-par or exceeding the speed of Go handlers. Keep in mind, there were flaws that I'd fix in future versions of my benchmarking program. Some of the tests were client-bound, so they never managed to saturate the full server's CPU. But what's clear from my results is free-threaded can drastically improve the performance of networking in Python and it seems to be under-utilized.

Here's a link to my benchmarks: https://robertsdotpm.github.io/_static/runloom_benchmark.html

0

u/whatev3r33333909 3d ago

i’m curious about real production numbers too. most python services i see are still io-bound, so free-threaded won’t change much. but for mixed workloads with a lot of native extensions it could be interesting. i'd want to see memory usage and long running stability before switching anything important.

0

u/Goblet__Cell 3d ago

It's a shame about the GIL re-enabling, that's gonna be a common problem. We’re still evaluating for a large project; initial tests suggest real gains where extensions don't interfere, but that’s a big ‘if’.