return to table of content

Free-threaded CPython is ready to experiment with

nine_k
123 replies
21h30m

Python 3 progress so far:

  [x] Async.
  [x] Optional static typing.
  [x] Threading.
  [ ] JIT.
  [ ] Efficient dependency management.

jolux
51 replies
21h20m

The successful languages without efficient dependency management are painful to manage dependencies in, though. I think Python should be shooting for a better package management user experience than C++.

Galanwe
27 replies
21h0m

Not sure this is still a valid critic of Python in 2024.

Between pip, poetry and pyproject.toml, things are now quite good IMHO.

arp242
21 replies
20h12m

I guess that depends from your perspective. I'm not a Python developer, but like many people I do want to run Python programs from time to time.

I don't really know Rust, or Cargo, but I never have trouble building any Rust program: "cargo build [--release]" is all I need to know. Easy. Even many C programs are actually quite easy: "./configure", "make", and optionally "make install". "./configure" has a nice "--help". There is a lot to be said about the ugly generated autotools soup, but the UX for people just wanting to build/run it without in-depth knowledge of the system is actually quite decent. cmake is a regression here.

With Python, "pip install" gives me an entire screen full of errors about venv and "externally managed" and whatnot. I don't care. I just want to run it. I don't want a bunch of venvs, I just want to install or run the damn program. I've taken to just use "pip install --break-system-packages", which installs to ~/.local. It works shrug.

Last time I wanted to just run a project with a few small modifications I had a hard time. I ended up just editing ~/.local/lib/python/[...] Again, it worked so whatever.

All of this is really where Python and some other languages/build systems fail. Many people running this are not $language_x programmers or experts, and I don't want to read up on every system I come across. That's not a reasonable demand.

Any system that doesn't allow non-users of that language to use it in simple easy steps needs work. Python's system is one such system.

simonw
12 replies
19h54m

"I don't want a bunch of venvs"

That's your problem right there.

Virtual environments are the Python ecosystem's solution to the problem of wanting to install different things on the same machine that have different conflicting requirements.

If you refuse to use virtual environments and you install more than one separate Python project you're going to run into conflicting requirements and it's going to suck.

Have you tried pipx? If you're just installing Python tools (and not hacking on them yourself) it's fantastic - it manages separate virtual environments for each of your installations without you having to think about them (or even know what a virtual environment is).

arp242
6 replies
19h30m

Managing a farm of virtualenvs and mucking about with my PATH doesn't address the user-installable problem at all. And it seems there's a new tool to try every few months that really will fix all problems this time.

And maybe if you're a Python developer working on the code every day that's all brilliant. But most people aren't Python developers, and I just want to try that "Show HN" project or whatnot.

Give me a single command I can run. Always. For any project. And that always works. If you don't have that then your build system needs work.

simonw
3 replies
17h56m

"Give me a single command I can run. Always. For any project. And that always works."

    pipx install X

arp242
1 replies
10h53m

Right so; I'll try that next time. Thanks. I just go by the very prominent "pip install X" on every pypi page (as well as "pip install .." in many READMEs).

simonw
0 replies
4h22m

Yeah, totally understand that - pipx is still pretty poorly known by people who are active in Python development!

A few of my READMEs start like this: https://github.com/simonw/paginate-json?tab=readme-ov-file#i...

    ## Installation

    pip install paginate-json

    Or use pipx (link to pipx site)

    pipx install paginate-json
But I checked and actually most them still don't even mention it. I'll be fixing that in the future.

pletnes
0 replies
6h9m

Pipx is great! Although, I always seem to have to set up PATH, at least on windows?

zo1
0 replies
7h32m

I could say the exact same stuff about NodeJs, c++, go, rust, php, etc. All of these are easy to use and debug and "install easily" when you know them and use them regularly, and the opposite if you're new. Doubly-so if you personally don't like that language or have some personal pet peeve about it's choices.

Guys let's not pretend like this is somehow unique to python. It's only until about a few years ago that it was incredibly difficult to install and use npm on windows. Arguably the language ecosystem with the most cumulative hipster-dev hours thrown at it, and it still was a horrible "dev experience".

Ringz
0 replies
13h1m

That single command is pipx.

guhidalg
2 replies
19h28m

Normal users who just want to run some code shouldn't need to learn why they need a venv or any of its alternatives. Normal users just want to download a package and run some code without having to think about interfering with other packages. Many programming languages package managers give them that UX and you can't blame them for expecting that from Python. The added step of having to think about venvs with Python is not good. It is a non-trivial system that every single Python user is forced to learn, understand, and the continually remember every time they switch from one project to another.

simonw
0 replies
17h56m

I agree with that. Until we solve that larger problem, people need to learn to use virtual environments, or at least learn to install Python tools using pipx.

nine_k
0 replies
18h7m

This is correct. The whole application installation process, including the creation of a venv, installing stuff into it, and registering it with some on-PATH launcher should be one command.

BTW pyenv comes relatively close.

kallapy
0 replies
6h33m

I reject the virtual environments and have no issues. On an untrusted machine (see e.g. the recent token leak):

  /a/bin/python3 -m pip install foo
  /b/bin/python3 -m pip install bar
The whole venv thing is overblown but a fertile source for blogs and discussions. If C-extensions link to installed libraries in site-packages, of course they should use RPATH.

imtringued
0 replies
26m

Pythons venvs are a problem to the solution of solving the dependency problem. Consider the following: it is not possible to relocate venvs. In what universe does this make sense? Consider a C++ or Rust binary that would only run when it is placed in /home/simonw/.

vhcr
4 replies
19h23m

Do you have a problem with Node.js too because it creates a node_modules folder, or is the problem that it is not handled automatically?

arp242
3 replies
19h11m

I don't care about the internals. I care about "just" being able to run it.

I find that most JS projects work fairly well: "npm install" maybe followed by "npm run build" or the like. This isn't enforced by npm and I don't think npm is perfect here, but practical speaking as a non-JS dev just wanting to run some JS projects: it works fairly well for almost all JS projects I've wanted to run in the last five years or so.

A "run_me.py" that would *Just Work™" is fine. I don't overly care what it does internally as long as it's not hugely slow or depends on anything other than "python". Ideally this should be consistent throughout the ecosystem.

To be honest I can't imagine shipping any project intended to be run by users and not have a simple, fool-proof, and low-effort way of running it by anyone of any skill level, which doesn't depend on any real knowledge of the language.

sgarland
2 replies
18h53m

To be honest I can't imagine shipping any project intended to be run by users and not have a simple, fool-proof, and low-effort way of running it by anyone of any skill level, which doesn't depend on any real knowledge of the language.

This is how we got GH Issues full of inane comments, and blogs from mediocre devs recommending things they know nothing about.

I see nothing wrong with not catering to the lowest common denominator.

arp242
1 replies
18h39m

Like people with actual lives to live and useful stuff to do that's not learning about and hand-holding a dozen different half-baked build systems.

But sure, keep up the cynical illusion that everyone is an idiot if that's what you need to go through life.

sgarland
0 replies
18h26m

I didn’t say that everyone is an idiot. I implied that gate keeping is useful as a first pass against people who are unlikely to have the drive to keep going when they experience difficulty.

When I was a kid, docs were literally a book. If you asked for help and didn’t cite what you had already tried / read, you’d be told to RTFM.

Python has several problems. Its relative import system is deranged, packaging is a mess, and yes, on its face needing to run a parallel copy of the interpreter to pip install something is absurd. I still love it. It’s baked into every *nix distro, a REPL is a command away, and its syntax is intuitive.

I maintain that the relative ease of JS – and more powerfully, Node – has created a monstrous ecosystem of poorly written software, with its adherents jumping to the latest shiny every few months because this time, it’s different. And I _like_ JS (as a frontend language).

Galanwe
1 replies
13h7m

Maybe I am biased, because I learned these things so long ago and I don't realize that it's a pain to learn. But what exactly is so confusing about virtualenvs ?

They really not that different from any other packaging system like JS or Rust. The only difference is instead of relying on your current directory to find the the libraries / binaries (and thus requiring you to wrap binaries call with some wrapper to search in a specific path), they rely on you sourcing an `activate` script. That's really just it.

Create a Virtualenv:

    $ virtualenv myenv
Activate it, now it is added to your $PATH:

    $ . myenv/bin/activate
There really is nothing more in the normal case.

If you don't want to have to remember it, create a global Virtualenv somewhere, source it's activate in your .bashrc, and forget it ever existed.

imtringued
0 replies
24m

Only python demands you to source an activation script before doing anything.

sgarland
0 replies
18h56m

This is mostly a curse of Python’s popularity. The reason you can’t pip install with system Python is that it can break things, and when your system is relying on Python to run various tools, that can’t be allowed. No one (sane) is building OS-level scripts with Node.

The simplest answer, IMO, is to download the Python source code, build it, and then run make altinstall. It’ll install in parallel with system Python, and you can then alias the new executable path so you no longer have to think about it. Assuming you already have gcc’s tool chain installed, it takes roughly 10-15 minutes to build. Not a big deal.

Arcanum-XIII
3 replies
20h51m

All is well, then, one day, you have to update one library.

Some days later, in some woods or cave, people will hear your screams of rage and despair.

Galanwe
2 replies
20h43m

Been using python for 15 years now, and these screams were never heard.

Dev/test with relaxed pip installs, freeze deployment dependencies with pip freeze/pip-tools/poetry/whateveryoulike, and what's the problem?

neeleshs
0 replies
20h8m

same here. Been using python/pip for 10+ years and this was never a problem. In the java world, there is jar hell, but it was never a crippling issue, but a minor annoyance once a year or so.

In general, is dependency management such a massive problem it is made to be on HN? Maybe people here are doing far more complex/different things than I've done in the past 20 years

mixmastamyk
0 replies
16h18m

Guessing that folks who write such things are lacking sysad skills like manipulating paths, etc.

It does take Python expertise to fix other issues on occasion but they are fixable. Why I think flags like ‘pip —break-system-packages’ are silly. It’s an optimization for non-users over experienced ones.

pletnes
0 replies
7h17m

This is the truth right here. The issues are with people using (not officially) deprecated tools and workflows, plus various half baked scripts that solved some narrow use cases.

yosefk
15 replies
21h14m

If Python's dependency management is better than anything, it's better than C++'s. Python has pip and venv. C++ has nothing (you could say less than nothing since you also have ample opportunity for inconsistent build due to mismatching #defines as well as using the wrong binaries for your .h files and nothing remotely like type-safe linkage to mitigate human error. It also has an infinite number of build systems where each system of makefiles or cmakefiles is its own build system with its own conventions and features). In fact python is the best dependency management system for C++ code when you can get binaries build from C++ via pip install...

stavros
9 replies
19h20m

That was the entire point, that C++ is the absolute worst.

dgfitz
8 replies
17h25m

I pip3 installed something today. It didn’t work, at all.

I then yum installed a lib and headers, it worked well.

C++ on an msft platform is the worst. I can’t speak for Mac. C++ on a linux is quite pleasant. Feels like most of the comments like yours are biased for un-stated reasons.

stavros
5 replies
17h22m

If I had a penny for every time I gave up on compiling C++ software because there's no way to know what dependencies it needs, I'd be a millionaire. Python at least lists them.

ahartmetz
3 replies
9h47m

Is that because the compiler failed with "foo.h not found" or the build system said "libfoo not found"? CMake is most common and it will tell you. Worst case it's difficult to derive the package name from the name in the diagnostic.

It's not great, but usually not a big deal neither IME. Typically a couple of minutes to e.g. find that required libSDL2 addon module or whatever, if there is that kind of problem at all.

stavros
2 replies
8h34m

Yes it is, and it's usually such a big deal for me that I just don't use that software. I don't have time to go through a loop of "what's the file name? What package is it in? Install, repeat". This is by far the worst experience I've had with any language. Python has been a breeze in comparison.

dgfitz
1 replies
5h20m

I’m not going to refute your points. If you’re going to wear rose-tinted glasses about all of the bad parts about python, that’s fine, I also like python.

stavros
0 replies
5h19m

What's rose-tinted about "one of them downloads dependencies automatically, the other one doesn't"?

dgfitz
0 replies
16h52m

If I had a penny every time I heard something like that on sites like this, I’d be a billionaire :)

yupyupyups
0 replies
16h49m

Mac has the Brew project, which is sort of like apt-get or yum.

viraptor
0 replies
13h12m

This has nothing to do with languages. You can yum install python packages and expect them to work fine. You can install C++ files using an actual dependency manager like vcpkg or conan and have issues.

You're pointing out differences between software package management styles, not languages.

wiseowise
2 replies
20h26m

If Python's dependency management is better than anything, it's better than C++'s.

That’s like the lowest possible bar to clear.

yosefk
0 replies
10h28m

Agreed, but that was the bar set by the comment I was replying to, which claimed Python doesn't clear it.

vulnbludog
0 replies
8h57m

On bro

andmkl
1 replies
18h24m

C++ has apt-get etc. because the libraries do not change all the time. Also, of course there are vcpkg and conan.

Whenever you try to build something via pip, the build will invariably fail. The times that NumPy built from source from PyPI are long over. In fact, at least 50% of attempted package builds fail.

The alternative of binary wheels is flaky.

viraptor
0 replies
13h16m

C++ has apt-get

That's not a development dependency manager. System package management is a different kind of issue, even if there's a bit of overlap.

because the libraries do not change all the time

That's not true in practice. Spend enough time with larger projects or do some software packaging and you'll learn that the pain is everywhere.

__MatrixMan__
4 replies
19h27m

Python's dependency management sucks because they're audacious enough to attempt packaging non-python dependencies. People always bring Maven up as a system that got it right, but Maven only does JVM things.

I think the real solution here is to just only use python dependency management for python things and to use something like nix for everything else.

pletnes
1 replies
7h19m

This is what we used to have and it was much worse. Source: lived that life 10-15 y ago.

__MatrixMan__
0 replies
2h58m

15y ago I was using apt-get to manage my c++ dependencies with no way of keeping track of which dependency went with which project. It was indeed pretty awful.

Now when I cd into a project, direnv + nix notices the dependencies that that project needs and makes them available, whatever their language. When I cd into a different project, I get an entirely different set of dependencies. There's pretty much nothing installed with system scope. Just a shell and an editor.

Both of these are language agnostic, but the level of encapsulation is quite different and one is much better than that other. (There are still plenty of problems, but they can be fixed with a commit instead of a change of habit.)

The idea that every language needs a different package manager and that each of those needs to package everything that might my useful when called from that language whether or not it is written in that language... It just doesn't scale.

adgjlsfhk1
1 replies
19h13m

Julia's package manager (for one) works great and can manage non Julia packages. the problem with python's system is that rejecting semver makes writing a package manager basically impossible since there is no way to automatically resolve packages.

woodruffw
0 replies
17h30m

Could you clarify what you mean? pip and every other Python package installer is absolutely doing automatic package resolution, and the standard (PEP 440) dependency operators include a compatible version operator (~=) that's predicated on SemVer-style version behavior.

galdosdi
0 replies
25m

The shitshow that is python tooling is one of the reasons I prefer java jobs to python jobs when I can help it. Java got this pretty right years and years and years earlier. Why are python and javascript continuing to horse around playing games?

est
0 replies
20h40m

Deps in CPython are more about .so/.dll problem, not much can be done since stuff happens outside python itself.

GTP
48 replies
21h14m

I don't get how this optional static typing works. I had a quick look at [1], and it begins with a note saying that Python's runtime doesn't enforce types, leaving the impression that you need to use third-party tools to do actual type checking. But then it continues just like Python does the check. Consider that I'm not a Python programmer, but the main reason I stay away from it is the lack of a proper type system. If this is going to change, I might reconsider it.

[1] https://docs.python.org/3/library/typing.html

VeejayRampay
16 replies
20h41m

python will never be "properly typed"

what it has is "type hints" which is way to have richer integration with type checkers and your IDE, but will never offer more than that as is

hot_gril
12 replies
20h37m

It is properly typed: it has dynamic types :)

GTP
11 replies
20h23m

Then we have very different ideas of what proper typing is :D Look at this function, can you tell me what it does?

  def plus(x, y):
    return x+y
If your answer is among the lines of "It returns the sum x and y" then I would ask you who said that x and y are numbers. If these are strings, it concatenates them. If instead you pass a string and a number, you will get a runtime exception. So not only you can't tell what a function does just by looking at it, you can't even know if the function is correct (in the sense that will not raise an exception).

hot_gril
2 replies
20h11m

When is the last time you had a bug IRL caused by passing the wrong kind of thing into plus(x, y), which your tests didn't catch?

GTP
1 replies
1h38m

It never happened to me, because I don't use Python ;)

On a more serious note, your comment actually hints at an issue: unit testing is less effective without static type checking. Let's assume I would like to sum x and y. I can extensively test the function and see that it indeed correctly sums two numbers. But then I need to call the function somewhere in my code, and whether it will work as intended or not depends on the context in which the function is used. Sometimes the input you pass to a function depends from some external source outside your control, an if that's the case you have to resort to manual type checking. Or use a properly typed language.

hot_gril
0 replies
1h23m

This isn't an actual problem people encounter in unit testing, partially because you test your outer interfaces first. Also, irl static types often get so big and polymorphic that the context matters just as much.

andrewaylett
2 replies
20h11m

It calls x.__add__(y).

Python types are strictly specified, but also dynamic. You don't need static types in order to have strict types, and indeed just because you've got static types (in TS, for example) doesn't mean you have strict types.

A Python string is always a string, nothing is going to magically turn it into a number just because it's a string representation of a number. The same (sadly) can't be said of Javascript.

hot_gril
0 replies
19h22m

Yeah and even with static typing, a string can be many things. Some people even wrap their strings into singleton structs to avoid something like sending a customerId string into a func that wants an orderId string, which I think is overkill. Same with int.

GTP
0 replies
2h7m

It calls x.__add__(y)

Your answer doesn't solve the problem, it just moves it: can you tell me what x. __add__(y) does?

vhcr
1 replies
19h19m

In theory it's nice that the compiler would catch those kinds of problems, but in practice it doesn't matters.

GTP
0 replies
2h2m

It can matter also in practice. Once I was trying some Python ML model to generate images. My script ran for 20 minutes to then terminate with an exception when it arrived at the point of saving the result to a file. The reason is that I wanted to concatenate a counter to the file name, but forgot to wrap the integer into a call to str(). 20 minutes wasted for an error that other languages would have spotted before running the script.

mixmastamyk
1 replies
15h56m

you can't tell what a function does just by looking at it

You just did tell us what it does by looking at it, for the 90% case at least. It might be useful to throw two lists in there as well. Throw a custom object in there? It will work if you planned ahead with dunder add and radd. If not fix, implement, or roll back.

GTP
0 replies
1h57m

You just did tell us what it does by looking at it, for the 90% case at least

The problem is that you can't know if the function is going to do what you want it to do without also looking at the context in which it is used. And what you pass as input could be dependent on external factors that you don't control. So I prefer the languages that let me know what happens in 100% of the cases.

nequo
0 replies
20h5m

Both Haskell and OCaml can raise exceptions for you, yet most people would say that they are properly typed.

The plus function you wrote is not more confusing than any generic function in a language that supports that.

infamia
1 replies
17h52m

what it has is "type hints" which is way to have richer integration with type checkers and your IDE, but will never offer more than that as is

Python is strongly typed and it's interpreter is type aware of it's variables, so you're probably overreaching with that statement. Because Python's internals are type aware, it's how folks are able to create type checkers like mypy and pydantic both written in Python. Maybe you're thinking about TS/JSDoc, which is just window dressing for IDEs to display hints as you described?

GTP
0 replies
2h9m

I don't think you can say that a language is strongly typed if only the language's internals are. The Python interpreter prevents you from summing an integer to a string, but only at runtime when in many cases it's already too late. A strongly typed language would warn you much sooner.

kortex
0 replies
17h17m

s/will never be/already is/g

https://github.com/mypyc/mypyc

You can compile python to c. Right now. Compatibility with extensions still needs a bit of work. But you can write extremely strict python.

That's without getting into things like cython.

zitterbewegung
8 replies
21h6m

Optional static typing is just like a comment (real term is annotation) of the input variable(s) and return variable(s). No optimization is performed. Using a tool such as mypy that kicks off on a CI/CD process technically enforces types but they are ignored by the interpreter unless you make a syntax error.

nine_k
6 replies
21h2m

A language server in your IDE kicks in much earlier, and is even more helpful.

zitterbewegung
5 replies
17h48m

I haven't used an IDE that has that but it is still just giving you a hint that there is an error and the interpreter is not throwing an error which was my point.

kortex
2 replies
17h23m

That's true of most compiled languages. Unless we are talking about asserts, reflection, I think type erasure, and maybe a few other concepts, language runtimes don't check types. C does not check types at runtime. You compile it and then rely on control of invariants and data flow to keep everything on rails. In python, this is tricky because everything is behind at least one layer of indirection, and thus virtually everything is mutable, so it's hard to enforce total control of all data structures. But you can get really close with modern tooling.

cwalv
1 replies
15h45m

> and the interpreter is not throwing an error which was my point. > That's true of most compiled languages

True of most statically typed languages (usually no need to check at runtime), but not true in Python or other dynamically typed languages. Python would have been unusable for decades (prior to typehints) if that was true.

kortex
0 replies
2h9m

That's just reflection. That's a feature of code, not language runtime. I think there are some languages which in fact have type checking in the runtime as a bona-fide feature. Most won't, unless you do something like isinstance()

zo1
0 replies
1h37m

"I haven't used an IDE that has that but it is still just giving you a hint that there is an error and the interpreter is not throwing an error which was my point."

At this point, I'm not sure how one is to take your opinion on this matter. Just like me coding some C# or Java in notepad and then opining to a Java developer audience about the state of their language and ecosystem.

setopt
0 replies
7h46m

I haven't used an IDE that has that

You don’t need an IDE for this, an LSP plugin + Pyright is sufficient to get live type checking. For instance, Emacs (Eglot), Vim (ALE), Sublime (SublimeLSP) all support Pyright with nearly no setup required.

kortex
0 replies
17h27m

Nope. Type annotations can be executed and accessed by the runtime. That's how things like Pydantic, msgspec, etc, do runtime type enforcement and coercion.

There are also multiple compilers (mypyc, nuitka, others I forget) which take advantage of types to compile python to machine code.

hot_gril
8 replies
20h39m

I think static typing is a waste of time, but given that you want it, I can see why you wouldn't want to use Python. Its type-checking is more half-baked and cumbersome than other languages, even TS.

nine_k
3 replies
19h41m

I used to think like that until I tried.

There are areas where typing is more important: public interfaces. You don't have to make every piece of your program well-typed. But signatures of your public functions / methods matter a lot, and from them types of many internal things can be inferred.

If your code has a well-typed interface, it's pleasant to work with. If interfaces of the libraries you use are well-typed, you have easier time writing your code (that interacts with them). Eventually you type more and more code you write and alter, and keep reaping the benefits.

simonw
1 replies
19h33m

This was the thing that started to bring me around to optional typing as well. It makes the most sense to me as a form of documentation - it's really useful to know what types are expected (and returned) by a Python function!

If that's baked into the code itself, your text editor can show inline information - which saves you from having to go and look at the documentation yourself.

I've started trying to add types to my libraries that expose a public API now. I think it's worth the extra effort just for the documentation benefit it provides.

hot_gril
0 replies
19h29m

This is what made me give it a shot in TS, but the problem is your types at interface boundaries tend to be annoyingly complex. The other problem is any project with optional types soon becomes a project with required types everywhere.

There might be more merit in widely-used public libraries, though. I don't make those.

hot_gril
0 replies
19h39m

I shouldn't have said it's a waste of time period, cause every project I work on does have static typing in two very important places: the RPC or web API (OpenAPI, gRPC, whatever it is), and the relational database. But not in the main JS or Py code. That's all I've ever needed.

I did try migrating a NodeJS backend to TS along with a teammate driving that effort. The type-checking never ended up catching any bugs, and the extra time we spent on that stuff could've gone into better testing instead. So it actually made things more dangerous.

baq
1 replies
20h29m

Typescript is pretty much the gold standard, it’s amazing how much JavaScript madness you can work around just on the typechecking level.

IMHO Python should shamelessly steal as much typescript’s typing as possible. It’s tough since the Microsoft typescript team is apparently amazing at what they do so for now it’s a very fast moving target but some day…

hot_gril
0 replies
19h42m

Well the TS tooling is more painful in ways. It's not compatible with some stuff like the NodeJS profiler. Also there's some mess around modules vs "require" syntax that I don't understand fully but TS somehow plays a role.

grumpyprole
0 replies
18h55m

A type checker is only going to add limited value if you don't put the effort in yourself. If everything string-like is just a string, and if data is not parsed into types that maintain invariants, then little is being constrained and there is little to "check". It becomes increasingly difficult the more sophisticated the type system is, but in some statically typed languages like Coq, clever programmers can literally prove the correctness of their program using the type system. Whereas a unit test can only prove the presence of bugs, not their absence.

GTP
0 replies
20h19m

I instead think that the lack of static typing is a waste of time, since without it you can have programs that waste hours of computation due to an exception that would have been prevented by a proper type system ;)

nine_k
5 replies
21h3m

At MPOW most Python code is well-type-hinted, and mypy and pyright are very helpful at finding issues, and also for stuff like code completion and navigation, e.g. "go to the definition of the type of this variable".

Works pretty efficiently.

BTW, Typescript also does not enforce types at runtime. Heck, C++ does not enforce types at runtime either. It does not mean that their static typing systems don't help during at development time.

GTP
4 replies
20h44m

BTW, Typescript also does not enforce types at runtime. Heck, C++ does not enforce types at runtime either. It does not mean that their static typing systems don't help during at development time.

Speaking of C here as I don't have web development experience. The static type system does help, but in this case, it's the compiler doing the check at compile time to spare you many surprises at runtime. And it's part of the language's standard. Python itself doesn't do that. Good that you can use external tools, but I would prefer if this was part of Python's spec.

Edit: these days I'm thinking of having a look at Mojo, it seems to do what I would like from Python.

kortex
3 replies
17h30m

https://github.com/python/mypy

Python itself doesn't do that

The type syntax is python. MyPy is part of Python. It's maintained by the python foundation. Mypy is not part of CPython because modularity is good, the same way that ANSI C doesn't compile anything, that's what gcc, clang, etc are for.

Mojo is literally exactly the same way, the types are optional, and the tooling handles type checking and compilation.

GTP
2 replies
2h20m

Mojo is literally exactly the same way.

No, because in Mojo, type checking is part of the language specification: you need no external tool for that. Python defines a syntax that can be used for type checking, but you need an external tool to do that. GCC does type checking because it's defined in the language specification. You would have a situation analogous to Python only if you needed GCC + some other tool for type checking. This isn't the case.

zo1
1 replies
1h41m

You're really splitting hairs here all to prove some sort of "type checking isn't included with python" property. Even if you're technically right, half the world really doesn't care and most code being churned out in Python is type-hinted, type-checked, and de-facto enforced at design time.

It's honestly winning the long-term war because traditional languages have really screwed things up with infinite and contrived language constructs and attempts just to satisfy some "language spec" and "compiler", whilst still trying to be expressive enough for what developers need and want to do safely. Python side-stepped all of that, has the perfect mix of type-checking and amazing "expressibility", and is currently proving that it's the way forward with no stopping it.

GTP
0 replies
1h30m

I'm not saying that no one should use Python, I'm just saying why I don't like it. But if you instead like it I will not try to stop you using it.

This said, if most people use type hints and the proper tooling to enforce type checking, I would say this would be a good reason to properly integrate (optional) static typing in the language: it shows that most programmers like static typing. The problem I focused on in my example isn't the only advantage of a type system.

sveiss
2 replies
21h6m

The parser supports the type hint syntax, and the standard library provides various type hint related objects.

So you can do things like “from typing import Optional” to bring Optional into scope, and then annotate a function with -> Optional[int] to indicate it returns None or an int.

Unlike a system using special comments for type hints, the interpreter will complain if you make a typo in the word Optional or don’t bring it into scope.

But the interpreter doesn’t do anything else; if you actually return a string from that annotated function it won’t complain.

You need an external third party tool like MyPy or Pyre to consume the hint information and produce warnings.

In practice it’s quite usable, so long as you have CI enforcing the type system. You can gradually add types to an existing code base, and IDEs can use the hint information to support code navigation and error highlighting.

quotemstr
1 replies
14h40m

In practice it’s quite usable

It would be super helpful if the interpreter had a type-enforcing mode though. All the various external runtime enforcement packages leave something to be desired.

setopt
0 replies
7h52m

I agree. There are usable third-party runtime type checkers though. I like Beartype, which lets you add a decorator @beartype above any function or method, and it’ll complain at runtime if arguments or return values violate the type hints.

I think runtime type checking is in some ways a better fit for a highly dynamic language like Python than static type checking, although both are useful.

wk_end
1 replies
21h10m

The interpreter does not and probably never will check types. The annotations are treated as effectively meaningless at runtime. External tools like mypy can be run over your code and check them.

cwalv
0 replies
15h42m

It checks types .. it doesn't check type annotations.

Just try:

  $ Python
  >>> 1 + '3'

davepeck
0 replies
21h8m

Third party tools (mypy, pyright, etc) are expected to check types. cpython itself does not. This will run just fine:

python -c "x: int = 'not_an_int'"

My opinion is that with PEP 695 landing in Python 3.12, the type system itself is starting to feel robust.

These days, the python ecosystem's key packages all tend to have extensive type hints.

The type checkers are of varying quality; my experience is that pyright is fast and correct, while mypy (not having the backing of a Microsoft) is slower and lags on features a little bit -- for instance, mypy still hasn't finalized support for PEP 695 syntax.

NegativeK
0 replies
17h24m

Python's typing must accommodate Python's other goal as quick scripting language. The solution is to document the optional typing system as part of the language's spec and let other tools do the checking, if a user wants to use them.

The other tools are trivially easy to set up and run (or let your IDE run for you.) As in, one command to install, one command to run. It's an elegant compromise that brings something that's sorely needed to Python, and users will spend more time loading the typing spec in their browser than they will installing the type checker.

whoiscroberts
5 replies
16h1m

Optional static typing, not really. Those type hints are not used at runtime for performance. Type hint a var as a string then set it to an init, that code still gonna try to execute.

zarzavat
4 replies
6h52m

Those type hints are not used at runtime for performance.

This is not a requirement for a language to be statically typed. Static typing is about catching type errors before the code is run.

Type hint a var as a string then set it to an int, that code still gonna try to execute.

But it will fail type checking, no?

mondrian
3 replies
3h51m

The critique is that "static typing" is not really the right term to use, even if preceded by "optional". "Type hinting" or "gradual typing" maybe.

In static typing the types of variables don't change during execution.

zarzavat
2 replies
3h13m

If there’s any checking of types before program runs then it’s static typing. Gradual typing is a form of static typing that allows you to apply static types to only part of the code.

I’m not sure what you mean by variables not changing types during execution in statically typed languages. In many statically typed languages variables don’t exist at runtime, they get mapped to registers or stack operations. Variables only exist at runtime in languages that have interpreters.

Aside from that, many statically typed languages have a way to declare dynamically typed variables, e.g. the dynamic keyword in C#. Or they have a way to declare a variable of a top type e.g. Object and then downcast.

neonsunset
0 replies
2h42m

'dynamic' in C# is considered a design mistake and pretty much no codebase uses it.

On the other hand F# is much closer to the kind of gradual typing you are discussing.

mondrian
0 replies
2h2m

Python is dynamically typed because it type-checks at runtime, regardless of annotations or what mypy said.

ramses0
3 replies
20h45m

You forgot:

    [X] print requires parentheses

zarzavat
0 replies
6h52m

print was way better when it was a statement.

vulnbludog
0 replies
8h53m

Idk why but python 2 print still pops up in my nightmares lol on bro

nine_k
0 replies
15h23m

Fair. But it was importable from __future__ back in 2.7.

VeejayRampay
3 replies
20h43m

the efficient dependency management is coming, the good people of astral will take care of that with the uv-backed version of rye (initially created by Armin Ronacher with inspirations from Cargo), I'm really confident it'll be good like ruff and uv were good

noisy_boy
2 replies
17h17m

rye's habit of insisting on creating a .venv per project is a deal-breaker. I don't want .venvs spread all over my projects eating into space (made worse by the ml/LLM related mega packages). It should atleast respect activated venvs.

nine_k
0 replies
15h17m

A venv per project is a very sane way. Put them into the ignore file. Hopefully they also could live elsewhere in the tree.

VeejayRampay
0 replies
7h17m

well that's good for you, but you're in the minority and rye will end up being a standard anyway, just like uv and ruff, because they're just so much better than the alternatives

alfalfasprout
2 replies
21h8m

The conda-forge ecosystem is making big strides in dependency management. No more are we stuck with the abysmal pip+venv story.

nhumrich
1 replies
14h9m

Python 3.12 introduces a little bit of JIT. Also, there is always pypy.

For efficient dependency management, there is now rye and UV. So maybe you can check all those boxes?

nine_k
0 replies
12h43m

Rye is pretty alpha, uv is young, too, and they are not part of "core" Python, not under the Python Foundation umbrella (like e.g. mypy is).

So there's plenty of well-founded hope, but the boxes are still not checked.

agumonkey
1 replies
20h21m

I'm eager to see what a simple JIT can bring to computing energy savings on python apps.

KeplerBoy
0 replies
11h47m

I'd wager the energy savings could put multiple power plants out of service.

I regularly encounter python code which takes minutes to execute but runs in less than a second when replacing key parts with compiled code.

Sparkyte
63 replies
21h0m

My body is ready. I love python because the ease of writing and logic. Hopefully the more complicated free-threaded approach is comprehensive enough to write it like we traditionally write python. Not saying it is or isn't I just haven't dived enough into python multithreading because it is hard to put those demons back once you pull them out.

hot_gril
33 replies
20h26m

What are the common use cases for threading in Python? I feel like that's a lower level tool than most Python projects would want, compared to asyncio or multiprocessing.Pool. JS is the most comparable thing to Python, and it got pretty darn far without threads.

BugsJustFindMe
27 replies
20h23m

Working with asyncio sucks when all you want is to be able to do some things in the background, possibly concurrently. You have to rewrite the worker code using those stupid async await keywords. It's an obnoxious constraint that completely breaks down when you want to use unaware libraries. The thread model is just a million times easier to use because you don't have to change the code.

hot_gril
19 replies
20h20m

Asyncio is designed for things like webservers or UIs where some framework is probably already handling the main event loop. What are you doing where you just want to run something else in the background, and IPC isn't good enough?

BugsJustFindMe
18 replies
20h17m

Non-blocking HTTP requests is an extremely common need, for instance. Why the hell did we need to reinvent special asyncio-aware request libraries for it? It's absolute madness. Thread pools are much easier to work with.

where some framework is probably already handling the main event loop

This is both not really true and also irrelevant. When you need a flask (or whatever) request handler to do parallel work, asyncio is still pretty bullshit to use vs threads.

hot_gril
13 replies
20h10m

Non-blocking HTTP request is the bread and butter use case for asyncio. Most JS projects are doing something like this, and they don't need to manage threads for it. You want to manage your own thread pool for this, or are you going to spawn and kill a thread every time you make a request?

BugsJustFindMe
12 replies
20h6m

Non-blocking HTTP request is the bread and butter use case for asyncio

And the amount of contorting that has to be done for it in Python would be hilarious if it weren't so sad.

Most JS projects

I don't know what JavaScript does, but I do know that Python is not JavaScript.

You want to manage your own thread pool for this...

In Python, concurrent futures' ThreadPoolExecutor is actually nice to use and doesn't require rewriting existing worker code. It's already done, has a clean interface, and was part of the standard library before asyncio was.

hot_gril
6 replies
19h47m

ThreadPoolExecutor is the most similar thing to asyncio: It hands out promises, and when you call .result(), it's the same as await. JS even made its own promises implicitly compatible with async/await. I'm mentioning what JS does because you're describing a very common JS use case, and Python isn't all that different.

If you have async stuff happening all over the place, what do you use, a global ThreadPoolExecutor? It's not bad, but a bit more cumbersome and probably less efficient. You're running multiple OS threads that are locking, vs a single-threaded event loop. Gets worse the more long-running blocking calls there are.

Also, I was originally asking about free threads. GIL isn't a problem if you're just waiting on I/O. If you want to compute on multiple cores at once, there's multiprocessing, or more likely you're using stuff like numpy that uses C threads anyway.

BugsJustFindMe
5 replies
19h35m

Python isn't all that different

Again, Python's implementation of asyncio does not allow you to background worker code without explicitly altering that worker code to be aware of asyncio. Threads do. They just don't occupy the same space.

Also, I was originally asking about free threads...there's multiprocessing

Eh, the obvious reason to not want to use separate processes is a desire for some kind of shared state without the cost or burden of IPC. The fact that you suggested multiprocessing.Pool instead of concurrent_futures.ProcessPoolExecutor and asked about manual pool management feels like it tells me a little bit about where your head is at here wrt Python.

hot_gril
4 replies
19h15m

Basically true in JS too. You're not supposed to do blocking calls in async code. You also can't "await" an async call inside a non-async func, though you could fire-and-forget it.

Right, but how often does a Python program have complex shared state across threads, rather than some simple fan-out-fan-in, and also need to take advantage of multiple cores?

nilamo
3 replies
18h31m

The primary thing that tripped me up about async/await, specifically only in Python, is that the called function does not begin running until you await it. Before that moment, it's just an unstarted generator.

To make background jobs, I've used the class-based version to start a thread, then the magic method that's called on await simply joins the thread. Which is a lot of boilerplate to get a little closer to how async works in (at least) js and c#.

pests
0 replies
16h27m

That is a common split in language design decisions. I think the argument for the python-style where you have to drive it to begin is more useful as you can always just start it immediately but also let's you delay computation or pass it around similar to a Haskell thunk.

LegionMammal978
0 replies
17h59m

Rust's version of async/await is the same in that respect, where futures don't do anything until you poll them (e.g., by awaiting them): if you want something to just start right away, you have to call out to the executor you're using, and get it to spawn a new task for it.

Though to be fair, people complain about this in Rust as well. I can't comment much on it myself, since I haven't had any need for concurrent workloads that Rayon (a basic thread-pool library with work stealing) can't handle.

stavros
4 replies
19h51m

I feel you. I know asyncio is "the future", but I usually just want to write a background task, and really hate all the gymnastics I have to do with the color of my functions.

BugsJustFindMe
3 replies
19h44m

I feel like "asyncio is the future" was invented by the same people who think it's totally normal to switch to a new javascript web framework every 6 months.

hot_gril
1 replies
19h27m

JS had an event loop since the start. It's an old concept that Python seems to have lifted, as did Rust. I used Python for a decade and never really liked the way it did threads.

zo1
0 replies
1h15m

Python's reactor pattern, or event loop as you call it, started with the "Twisted" framework or library. And that was first published in 2003. That's a full 6 years before Node.js was released which I assume was the first time anything event-loopy started happening in the JS world.

I forgot to mention that it came into prominence in the Python world through the Tornado http server library that did the same thing. Slowly over time, more and more language features were added to give native or first-class-citizen support to what a lot of people were doing behind the scenes (in sometimes very contrived abuses of generator functions).

stavros
0 replies
19h23m

I agree, I find Go's way much easier to reason about. It's all just functions.

kristjansson
2 replies
20h1m

You don’t? concurrent.futures.ThreadPoolExecutor can get a lot done without touching async code.

BugsJustFindMe
1 replies
19h58m

I am a big advocate for ThreadPoolExecutor. I'm saying it's superior to asyncio. The person I'm responding to was asking why use threads when you can use asyncio instead.

kristjansson
0 replies
19h0m

Ach, I posted before I saw the rest of your thread, apologies.

Totally agree, concurrent.futures strikes a great balance. Enough to get work done, a bit more constrained than threads on their own.

Asyncio is a lot of cud to chew if you just want a background task in an otherwise sync application

d0mine
0 replies
14h7m

Ordinary CPython code releases GIL during blocking I/O. You can do http requests + thread pool in Python.

j1elo
5 replies
18h42m

So, in Rust they had threading since forever and they are now hyped with this new toy called async/await (and all the new problems it brings), while in Python they've had async/await and are now excited to see the possibilities of this new toy called threads (and all its problems). That's funny!

nvy
1 replies
16h39m

Being hyped for <feature other languages have had for years> is totally on-brand for the Rust community.

hot_gril
0 replies
1h3m

That sounds more like Golang (generics)

hot_gril
0 replies
1h12m

Python is more so in the same boat as Rust. Python asyncio was relatively recent.

dralley
0 replies
18h19m

Yes? They have different use cases which they are good at.

BugsJustFindMe
0 replies
18h21m

Well, Python had threads already. This is just a slightly different form of them behind the scenes.

throwaway81523
0 replies
12h52m

Yeah I've never liked the async stuff. I've used the existing theading library and it's been fine, for those programs that are blocked on i/o most of the time. The GIL hasn't been a problem. Those programs often ran on single core machines anyway. We would have been better off without the GIL in the first place, but we may be in for headaches by removing it now.

kstrauser
3 replies
18h54m

It’s hard to say because we’ve come up with a lot of ways to work around the fact that threaded Python has always sucked. Why? Because there’d been no demand to improve it. Why? Because no one used it. Why? Because it sucked.

I’m looking forward to seeing how people use a Python that can be meaningfully threaded. While It may take a bit to built momentum, I suspect that in a few years there’ll be obvious use cases that are widely deployed that no one today has even really considered.

hot_gril
2 replies
12h33m

Maybe a place to look for obvious use cases is in other languages. JS doesn't have threads, but Swift does. The reason I can't think of one is, free threads are most useful for full parallelism that isn't "embarrassingly parallel," otherwise IPC does fine.

So far, I've rarely seen that. Best example I deal with was a networking project with lots of communication across threads, and that one was too performance-sensitive to even use C++, let alone Py. Other things I can think of are OS programming which again has to be C or Rust.

kstrauser
1 replies
3h7m

That's the kind of thing I stumble across all the time. Indexing all the symbols in a codebase:

  results = Counter()
  for file in here.glob('*.py'):
      symbols = parse(file)
      results.update(symbols)
Scanning image metadata:

  for image in here.glob('*.png'):
      headers = png.Reader(image)
      ...
Now that I think about it, most of my use cases involve doing expensive things to all the files in a directory, but in ways where it'd be really sweet if I could do it all in the same process space instead of using a multiprocessing pool (which is otherwise an excellent way to skin that cat).

I've never let that stop me from getting the job done. There's always a way, and if we can't use tool A, then we'll make tool B work. It'll still be nice if it pans out that decent threading is at least an option.

hot_gril
0 replies
1h30m

These are "embarassingly parallel" examples that multiprocessing is ok for, though. There was always the small caveat that you can't pickle a file handle, but it wasn't a real problem. Threads are more useful if you have lots of shared state and mutexes.

I think these examples would also perform well with GIL'd threads, since the actual Python part is just waiting on blocking calls that do the expensive work.

bongodongobob
0 replies
20h20m

Same as any other language. Separating UI from calculations is my most common need for it.

ameliaquining
27 replies
20h51m

The semantic changes are negligible for authors of Python code. All the complexity falls on the maintainers of the CPython interpreter and on authors of native extension modules.

stavros
26 replies
19h53m

Well, I'm not looking forward to the day when I upgrade my Python and suddenly I have to debug a ton of fun race conditions.

dagenix
21 replies
19h7m

As I understand it, if your code would have race conditions with free threaded python, than it probably already has them.

stavros
20 replies
18h15m

Not when there's a global interpreter lock.

dagenix
14 replies
17h49m

Are you writing an extension or Python code?

If you are writing Python code, the GIL can already be dropped at pretty much any point and there isn't much way of controlling when. Iirc, this includes in the middle of things like +=. There are some operations that Python defines as atomic, but, as I recall, there aren't all that many.

In what way is the GIL preventing races for your use case?

stavros
11 replies
17h45m

I mean that, if the GIL didn't prevent races, it would be trivially removable. Races that are already there in people's Python code have probably been debugged (or at least they are tolerated), so there are some races that will happen when the GIL is removed, and they will be a surprise.

dagenix
9 replies
17h20m

The GIL prevents the corruption of Pythons internal structures. It's hard to remove because:

1. Lots of extensions, which can control when they release the GIL unlike regular Python code, depend on it 2. Removing the GIL requires some sort of other mechanism to protect internal Python stuff 3. But for a long time, such a mechanism was resisted by th Python team because all attempts to remove the GIL either made single threaded code slower or were considered too complicated.

But, as far as I understand, the GIL does somewhere between nothing and very little to prevent races in pure Python code. And, my rough understanding, is that removing the GIL isn't expected to really impact pure Python code.

pdonis
6 replies
16h43m

> removing the GIL isn't expected to really impact pure Python code.

If your Python code assumes it's just going to run in a single thread now, and it is run in a single thread without the GIL, yes, removing the GIL will make no difference.

dagenix
5 replies
16h35m

If your Python code assumes it's just going to run in a single thread now, and it is run in a single thread without the GIL, yes, removing the GIL will make no difference.

I'm not sure I understand your point.

Yes, singled thread code will run the same with or without the GIL.

My understanding, was that multi-threaded pure-Python code would also run more or less the same without the GIL. In that, removing the GIL won't introduce races into pure-Python code that is already race free with the GIL. (and that relatedly, pure-Python code that suffers from races without the GIL also already suffers from them with the GIL)

Are you saying that you expect that pure-Python code will be significantly impacted by the removal of the GIL? If so, I'd love to learn more.

pdonis
4 replies
15h47m

> removing the GIL won't introduce races into pure-Python code that is already race free with the GIL.

What do you mean by "race free"? Do you mean the code expects to be run in multiple threads and uses the tools provided by Python, such as locks, mutexes, and semaphores, to ensure thread safety, and has been tested to ensure that it is race free when run multi-threaded? If that is what you mean, then yes, of course such code will still be race free without the GIL, because it was never depending on the GIL to protect it in the first place.

But there is a lot of pure Python code out there that is not written that way. Removal of the GIL would allow such code to be naively run in multiple threads using, for example, Python's support for thread pools. Anyone under the impression that removing the GIL was intended to allow this sort of thing without any further checking of the code is mistaken. That is the kind of thing my comment was intended to exclude.

dagenix
3 replies
15h20m

But there is a lot of pure Python code out there that is not written that way. Removal of the GIL would allow such code to be naively run in multiple threads using, for example, Python's support for thread pools.

I guess this is what I don't understand. This code could already be run in multiple threads today, with a GIL. And it would be broken - in all the same ways it would be broken without a GIL, correct?

Anyone under the impression that removing the GIL was intended to allow this sort of thing without any further checking of the code is mistaken. That is the kind of thing my comment was intended to exclude.

Ah, so, is your point that removing the GIL will cause people to take non-multithread code and run it in multiple threads without realizing that it is broken in that context? That its not so much a technical change, but a change of perception that will lead to issues?

pdonis
2 replies
15h4m

> This code could already be run in multiple threads today, with a GIL.

Yes.

> And it would be broken - in all the same ways it would be broken without a GIL, correct?

Yes, but the absence of the GIL would make race conditions more likely to happen.

> is your point that removing the GIL will cause people to take non-multithread code and run it in multiple threads without realizing that it is broken in that context?

Yes. They could run it in multiple threads with the GIL today, but as above, race conditions might not show up as often, so it might not be realized that the code is broken. But also, with the GIL there is the common perception that Python doesn't do multithreading well anyway, so it's less likely to be used for that. With the GIL removed, I suspect many people will want to use multithreading a lot more in Python to parallelize code, without fully realizing the implications.

dagenix
1 replies
14h5m

Yes, but the absence of the GIL would make race conditions more likely to happen.

Does it though? I'm not saying it doesn't, I'm quite curious. Switching between threads with the GIL is already fairly unpredictable from the perspective of pure-Python code. Does it get significantly more troublesome without the GIL?

Yes. They could run it in multiple threads with the GIL today, but as above, race conditions might not show up as often, so it might not be realized that the code is broken. But also, with the GIL there is the common perception that Python doesn't do multithreading well anyway, so it's less likely to be used for that. With the GIL removed, I suspect many people will want to use multithreading a lot more in Python to parallelize code, without fully realizing the implications.

Fair

pdonis
0 replies
51m

> Switching between threads with the GIL is already fairly unpredictable from the perspective of pure-Python code.

But it still prevents multiple threads from running Python bytecode at the same time: in other words, at any given time, only one Python bytecode can be executing in the entire interpreter.

Without the GIL that is no longer true; an arbitrary number of threads can all be executing a Python bytecode at the same time. So even Python-level operations that only take a single bytecode now must be protected to be thread-safe--where under the GIL, they didn't have to be. That is a significant increase in the "attack surface", so to speak, for race conditions in the absence of thread safety protections.

(Note that this does mean that even multi-threaded code that was race-free with the GIL due to using explicit locks, mutexes, semaphores, etc., might not be without the GIL if those protections were only used for multi-bytecode operations. In practice, whether or not a particular Python operation takes a single bytecode or multiple bytecodes is not something you can just read off from the Python code--you have to either have intimate knowledge of the interpreter's internals or you have to explicitly disassemble each piece of code and look at the bytecode that is generated. Of course the vast majority of programmers don't do that, they just use thread safety protections for every data mutation, which will work without the GIL as well as with it.)

stavros
1 replies
17h18m

Hmm, that's interesting, thank you. I didn't realize extensions can control the GIL.

dagenix
0 replies
16h31m

Yup, I think its described here: https://docs.python.org/3/c-api/init.html#releasing-the-gil-....

My understanding, is that many extensions will release the GIL when doing anything expensive. So, if you are doing CPU or IO bound operations in an extension _and_ you are calling that operation in multiple threads, even with the GIL you can potentially fully utilize all of the CPUs in your machine.

pdonis
0 replies
16h47m

> if the GIL didn't prevent races, it would be trivially removable

Nobody is saying the GIL doesn't prevent races at all. We are saying that the GIL does not prevent races in your Python code. It's not "trivially removable" because it does prevent races in the interpreter's internal data structures and in operations that are done in a single Python bytecode, and there are a lot of possible races in those places.

Also, perhaps you haven't considered the fact that Python provides tools such as mutexes, locks, and semaphores to help you prevent races in your Python code. Python programmers who do write multi-threaded Python code (for example, code where threads spend most of their time waiting on I/O, which releases the GIL and allows other threads to run) do have to use these tools. Why? Because the GIL by itself does not prevent races in your Python code. You have to do it, just as you do with multi-threaded code in any language.

> Races that are already there in people's Python code have probably been debugged

Um, no, they haven't, because they've never been exposed to multi-threading. Most people's Python code is not written to be thread-safe, so it can't safely be parallelized as it is, GIL or no GIL.

d0mine
1 replies
14h12m

It is not about your code, it is about C extensions you are relying on. Without GIL, you can't even be sure that refcounting works reliably. Bugs in C extensions are always possible. No GIL makes them more likely. Even if you are not the author of C extension, you have to debug the consequences.

dudus
0 replies
11h25m

Does that mean rewriting all the extensions to Rust? Or maybe CPython itself?

Would that be enough to make Python no gill viable?

pdonis
4 replies
17h51m

The GIL does not prevent race conditions in your Python code. It only prevents race conditions in internal data structures inside the interpreter and in atomic operations, i.e., operations that take a single Python bytecode. But many things that appear atomic in Python code take more than one Python bytecode. The GIL gives you no protection if you do such operations in multiple threads on the same object.

pdonis
1 replies
16h53m

I'll respond there.

vulnbludog
0 replies
9h2m

I’m a dummy don’t know nothing about coding but I love HN usernames lol

matsemann
0 replies
9h0m

I think what many will experience, is that they want to switch to multithreading without GIL, but learn they have code that will have race conditions. But that don't have race conditions today, as it's run as multiple processes, and not threads.

For instance our webserver. It uses multiple processes. Each request then can modify some global variable, use as cache or whatever, and only after it's completely done handling the request the same process will serve a new request. But when people see the GIL is gone, they probably would like to start using it. Can handle more requests without spamming processes / using beefy computer with lots of RAM etc.

And then one might discover new race conditions one never really had before.

teaearlgraycold
3 replies
19h49m

It's kept behind a flag. Hopefully will be forever.

stavros
1 replies
19h23m

Oh, very interesting, that's a great solution then.

metadat
0 replies
19h10m

The article states the goal is to eventually (after some years of working out the major kinks and performance regressions) promote Free-Threaded Python to be the default cPython distribution.

ZhongXina
0 replies
20h53m

Precisely, ease of writing, not ease of reading (the whole project, not just a tiny snippet of code) or supporting it long-term.

eigenvalue
58 replies
21h12m

Really excited for this. Once some more time goes by and the most important python libraries update to support no GIL, there is just a tremendous amount of performance that can be automatically unlocked with almost no incremental effort for so many organizations and projects. It's also a good opportunity for new and more actively maintained projects to take market share from older and more established libraries if the older libraries don't take making these changes seriously and finish them in a timely manner. It's going to be amazing to saturate all the cores on a big machine using simple threads instead of dealing with the massive overhead and complexity and bugs of using something like multiprocessing.

phkahler
41 replies
20h56m

I feel like most things that will benefit from moving to multiple cores for performance should probably not be written in Python. OTH "most" is not "all" so it's gonna be awesome for some.

wongarsu
14 replies
19h19m

I often reach for python multiprocessing for code that will run $singleDigit number of times but is annoyingly slow when run sequentially. I could never justify the additional development time for using a more performant language, but I can easily justify spending 5-10 minutes making the embarrassingly parallel stuff execute in parallel.

throwaway81523
13 replies
12h58m

I've generally been able to deal with embarassing parallelism by just chopping up the input and running multiple processes with GNU Parallel. I haven't needed the multiprocessing module or free threading so far. I believe CPython still relies on various bytecodes to run atomically, which you get automatically with the GIL present. So I wonder if hard-to-reproduce concurrency bugs will keep surfacing in the free-threaded CPython for quite some time.

I feel like all of this is tragic and Python should have gone to a BEAM-like model some years ago, like as part of the 2 to 3 transition. Instead we get async wreckage and now free threading with its attendant hazards. Plus who knows how many C modules won't be expecting this.

robertlagrant
12 replies
12h3m

Async seems fine? What's wrong with it?

wesselbindt
10 replies
10h50m

I've always found the criticism leveled by the colored functions blog post a bit contrived. Yes, when you replace the words async/await with meaningless concepts I do not care about, it's very annoying to have to arbitrarily mark a function as blue or red. But when you replace the word "aync" with something like "expensive", or "does network calls", it becomes clear that "async/await" makes intrinsic properties about your code (e.g., is it a bad idea to put this call in a loop from a performance perspective) explicit rather than implicit.

In short, "await" gives me an extra piece of data about the function, without having to read the body of the function (and the ones it calls, and the ones they call, etc). That's a good thing.

There are serious drawbacks to async/await, and the red/blue blog post manages to list none of them.

EDIT: all of the above is predicated on the idea that reading code is harder than writing it. If you believe the opposite, then blue/red has a point.

pansa2
6 replies
9h57m

when you replace the word "aync" with something like "expensive", or "does network calls", it becomes clear that "async/await" makes intrinsic properties about your code explicit rather than implicit.

Do you think we should be annotating functions with `expensive` and/or `networking`? And also annotating all of their callers, recursively? And maintaining 4 copies of every higher-order function depending on whether the functions it calls are `expensive`, `networking`, neither or both?

No, we rely on documentation for those things, and IMO we should for `async` as well. The reason we can’t, and why `async`/`await` exist, is because of shortcomings (lack of support for stackful coroutines) in language runtimes. The best solution is to fix those shortcomings, not add viral annotations everywhere.

wesselbindt
3 replies
8h58m

So here I think we differ fundamentally in how we like to read code. I much prefer being able to quickly figure out things of interest about a function by glancing at its signature, rather than look at documentation, or worse, having to read the implementation of the function and the functions it calls (and so on, recursively).

For example, I much prefer a signature like

  def f(a: int) -> str:
over

  def f(a):
because it allows me to see, without reading the implementation of the function (or, if it exists, and I'm willing to bet on its reliability, the documentation), that it takes an integer, and gives me a string. And yes, this requires that I write viral type annotations on all my functions when I write them, but for me the bottleneck at my job is not writing the code, it's reading it. So that's a small upfront cost I'm very much willing to pay.

Do you think we should be annotating functions with `expensive` and/or `networking`? And also annotating all of their callers, recursively?

Yes, absolutely, and yes, absolutely. That's just being upfront and honest about an intrinsic property of those functions. A function calling a function that does network I/O by transitivity also does network I/O. I prefer code that's explicit over code that's implicit.

pansa2
2 replies
8h30m

Yes, absolutely, and yes, absolutely.

Fair enough, that's a valid philosophy, and one in which `async`/`await` makes perfect sense.

However, it's not Python's philosophy - a language with dynamic types, unchecked exceptions, and racy multithreading. In Python, `async`/`await` seems to be at odds with other language features - it feels like it's more at home in a language like Rust.

wesselbindt
1 replies
8h7m

I completely agree with you. However I've always found the dynamic typing approach to be a bit at odds with

  python3 -c "import this" | head -4 | tail -1
I think the fast and loose style that Python enables is perfect for small scripts and one off data science notebooks and the like. But having worked in large codebases which adopt the same style, and ones that avoid it through static typing and in some cases async/await, the difference in productivity I've noticed in both me and my collaborators is too stark for me to ignore.

I think I should've been more nuanced in my comments praising async/await. I believe that what I say is valid in large IO-bound applications which go beyond basic CRUD operations. In general it depends, of course.

pansa2
0 replies
7h44m

I think the fast and loose style that Python enables is perfect for small scripts and one off data science notebooks and the like.

Agreed - I only use Python for scripts like this, preferring statically-typed, AOT-compiled languages for larger programs.

That’s why I think Python should have adopted full coroutines - it should play to its strengths and stick to its fast-and-loose style. However, the people who decide how the language evolves are all employees of large companies using it for large codebases - their needs are very different from people who are only using Python for small scripts.

hiddew
1 replies
9h49m

The reason we can’t, and why `async`/`await` exist, is because of shortcomings (lack of support for stackful coroutines) in language runtimes

The JVM runtime has solved this problem neatly with virtual threads in my opinion. Run a web request in a virtual thread, and all blocking I/O is suddenly no longer blocking the OS thread, but yielding/suspending and giving and giving another virtual thread run time. And all that without language keywords that go viral through your program.

pansa2
0 replies
9h39m

Yes, this is similar to how Go works. IIRC the same approach was available in Python as a library, “greenlet”, but Python’s core developers rejected it in favour of `async`/`await`.

adament
1 replies
10h11m

But a synchronous function can and many do make network calls or write to files. It is a rather vague signal about the functions behavior as opposed to the lack of the IO monad in Haskell.

To me the difficulty is more with writing generic code and maintaining abstraction boundaries. Unless the language provides a way to generalise over asyncness of functions, we need a combinatorial explosion of async variants of generic functions. Consider a simple filter algorithm it needs versions for: (synchronous vs asynchronous iterator) times (synchronous vs asynchronous predicate). We end up with a pragmatic but ugly solution: provide 2 versions of each algorithm: an async and a sync, and force the user of the async one to wrap their synchronous arguments.

Similarly changing some implementation detail of a function might change it from a synchronous to an asynchronous function, and this change must now propagate through the entire call chain (or the function must start its own async runtime). Again we end up in a place where the most future proof promise to give for an abstraction barrier is to mark everything as async.

wesselbindt
0 replies
8h17m

But a synchronous function can and many do make network calls or write to files

This, for me, is the main drawback of async/await, at least as it is implemented in for example Python. When you call a synchronous function which makes network calls, then it blocks the event loop, which is pretty disastrous, since for the duration of that call you lose all concurrency. And it's a fairly easy footgun to set off.

It is a rather vague signal about the functions behavior as opposed to the lack of the IO monad in Haskell.

I'm happy you mentioned the IO monad! For me, in the languages people pay me to write in (which sadly does not include Haskell or F#), async/await functions as a poor man's IO monad.

Again we end up in a place where the most future proof promise to give for an abstraction barrier is to mark everything as async.

Yes, this is one way to write async code. But to me this smells the same as writing every Haskell program as a giant do statement because the internals might want to do I/O at some point. Async/await makes changing side-effect free internals to effectful ones painful, which pushes you in the direction of doing the I/O at the boundaries of your system (where it belongs), rather than all over the place in your call stack. In a ports-adapters architecture, it's perfectly feasible to restrict network I/O to your service layer, and leave your domain entirely synchronous. E.g. sth like

  async def my_service_thing(request, database):
    my_business_object = await database.get(request.widget_id)
    my_business_object.change_state(request.new_widget_color)    # some complicated, entirely synchronous computation
    await database.save(my_business_object)
Async/await pushes you to code in a certain way that I believe makes a codebase more maintainable, in a way similar to the IO monad. And as with the IO monad, you can subvert this push by making everything async (or writing everything in a do statement), but there's better ways of working with them, and judging them based on this subversion is not entirely fair.

ugly solution: provide 2 versions of each algorithm: an async and a sync

I see your point, and I think it's entirely valid. But having worked in a couple async codebases for a couple of years, the amount of stuff I (or one of my collaborators) have had to duplicate for this reason I think I can count on one hand. It seems that in practice this cost is a fairly low one.

gpderetta
0 replies
8h28m

Except that at least in python async doesn't mean that. Non async functions can do networking, block, or do expensive operations.

On the other hand async functions can be very cheap.

Again, which useful property does async protect?

DanielVZ
7 replies
20h12m

Usually performance critical code is written in cpp, fortran, etc, and then wrapped in libraries for Python. Python still has a use case for glue code.

andmkl
6 replies
18h6m

Yes, but then extensions can already release the GIL and use the simple and industrial strength std::thread, which is orders of magnitude easier to debug.

ipsod
4 replies
17h34m

Really? Cool.

I expected that dropping down to C/C++ would be a large jump in difficulty and quantity of code, but I've found it isn't, and the dev experience isn't entirely worse, as, for example, in-editor code-intelligence is rock solid and very fast in every corner of my code and the libraries I'm using.

If anyone could benefit from speeding up some Python code, I'd highly recommend installing cppyy and giving it a try.

woodson
3 replies
17h6m

Thanks, I haven’t come across cppyy! But I’ve worked with pybind11, which works well, too.

ipsod
2 replies
15h20m

Sure! I tried pybind11, and some other things. cppyy was the first I tried that didn't give me any trouble. I've been using it pretty heavily for about a year, and still no trouble.

nly
1 replies
9h8m

Last I checked cppyy didn't build any code with optimisations enabled (same as cling)

ipsod
0 replies
2h11m

It seems like you might be able to enable some optimizations with EXTRA_CLING_ARGS. Since it's based on cling, it's probably subject to whatever limitations cling has.

To be honest, I don't know much about the speed, as my use-case isn't speeding up slow code.

woodruffw
0 replies
17h35m

Concurrent operations exist at all levels of the software stack. Just because native extensions might want to release the GIL and use OS threads doesn't mean pure Python can't also want (or need) that.

(And as a side note: I have never, in around a decade of writing C++, heard std::thread described as "easy to debug.")

eigenvalue
6 replies
18h1m

I personally optimize more for development time and overall productivity in creating and refactoring, adding new features, etc. I'm just so much faster using Python than anything else, it's not even close. There is such an incredible world of great libraries easily available on pip for one thing.

Also, I've found that ChatGPT/Claude3.5 are much, much smarter and better at Python than they are at C++ or Rust. I can usually get code that works basically the first or second time with Python, but very rarely can do that using those more performant languages. That's increasingly a huge concern for me as I use these AI tools to speed up my own development efforts very dramatically. Computers are so fast already anyway that the ceiling for optimization of network oriented software that can be done in a mostly async way in Python is already pretty compelling, so then it just comes back again to developer productivity, at least for my purposes.

indigodaddy
4 replies
16h33m

Ever messed about with Claude and php?

lanstin
2 replies
16h27m

I don't think we are supposed to use HN for humor only posts.

saagarjha
0 replies
15h30m

You think wrong

jacob019
0 replies
16h20m

lol

indigodaddy
0 replies
3h42m

Why the downvotes? Totally serious question. Jesus Christ HN

goosejuice
0 replies
12h43m

Kind of sounds like you are optimizing for convenience :)

MBCook
3 replies
20h19m

But it would give you more headroom before rewriting for performance would make sense right? That alone could be beneficial to a lot of people.

rty32
2 replies
19h1m

I think it is beneficial to some people, but not a lot. My guess is that most Python users (from beginners to advanced users, including many professional data scientists) have never heard of GIL or thought of doing any parallelization in Python. Code that needs performance and would benefit from multithreading, usually written by professional software engineers, likely isn't written in Python in the first place. It would make sense for projects that can benefit from disabling GIL without a ton of changes. Remember it is not trivial to update single threaded code to use multithreading correctly.

in Python language specifically. Their library may have already done some form of parallelization under the hood

bdd8f1df777b
1 replies
14h19m

Code that needs performance and would benefit from multithreading, usually written by professional software engineers, likely isn't written in Python in the first place.

There are a lot of simple cases where multi-threading can easily triple or quadruple the performance.

Certhas
0 replies
6h14m

But multiprocessing can't?

I used to write a ton of MPI based parallel python. It's pretty straightforward. But one could easily imagine trying to improve the multiprocessing ergonomics rather than introducing threading. Obviously the people who made the choice to push forward with this are aware of these options, too. Still mildly puzzling to me why threads for Python are needed/reasonable.

jodrellblank
2 replies
15h26m

https://www.servethehome.com/wp-content/uploads/2023/01/Inte...

AMD EPYC 9754 with 128-cores/256-threads, and EPYC 9734 with 112-cores/224-threads. TomsHardware says they "will compete with Intel's 144-core Sierra Forest chips, which mark the debut of Intel's Efficiency cores (E-cores) in its Xeon data center lineup, and Ampre's 192-core AmpereOne processors".

What in 5 years? 10? 20? How long will "1 core should be enough for anyone using Python" stand?

d0mine
1 replies
14h26m

Number crunching code in Python (such as using numpy/pytorch) performs the vast vast majority of its calculations in C/Fortran code under the hood where GIL can be released. Single python process can use multiple CPUs.

There is code that may benefit from the free threaded implementation but it is not as often as it might appear and it is not without its own downsides. In general, GIL simplifies multithreaded code.

There were no-GIL Python implementations such as Jython, IronPython. They hadn't replaced CPython, Pypy implementation which use GIL i.e., other concerns dominate.

imachine1980_
0 replies
12h59m

Yes but jython am iron aren't the standard, and I feel the more relevant part is inertia, puppy is design whit lots of concern of compatibility, then being the new standard can totally make difference making both cases not a good comparison.

Derbasti
1 replies
14h46m

A thought experiment:

A piece of code takes 6h to develop in C++, and 1h to run.

The same algorithm takes 3h to code in Python, but 6h to run.

If I could thread-spam that Python code on my 24 core machine, going Python would make sense. I've certainly been in such situations a few times.

Certhas
0 replies
8h30m

C++ and python are not the only options though.

Julia is one that is gaining a lot of use in academia, but any number of modern, garbage collected compiled high level languages could probably do.

tho34234234
0 replies
13h52m

It's not just about "raw-flop performance" though; it affects even basic things like creating data-loaders that run in the background while your main thread is doing some hard ML crunching.

Every DL library comes with its own C++ backend that does this for now, but it's annoyingly inflexible. And dealing with GIL is a nightmare if you're dealing with mixed Python code.

jillesvangurp
0 replies
13h34m

Right now you are right. This is about taking away that argument. There's no technical reason for this to stay true. Other than that the process of fixing this is a lot of work of course. But now that the work has started, it's probably going to progress pretty steadily.

It will be interesting to see how this goes over the next few years. My guess is that a lot of lessons were learned from the python 2 to 3 move. This plan seems pretty solid.

And of course there's a relatively easy fix for code that can't work without a GIL: just do what people are doing today and just don't fork any threads in python. It's kind of pointless in any case with the GIL in place so not a lot of code actually depends on threads in python.

Preventing the forking of threads in the presence of things still requiring the GIL sounds like a good plan. This is a bit of meta data that you could build into packages. This plan is actually proposing keeping track of what packages work without a GIL. So, that should keep people safe enough if dependency tools are updated to make use of this meta data and actively stop people from adding thread unsafe packages when threading is used.

So, I have good hopes that this is going to be a much smoother transition than python 2 to 3. The initial phase is probably going to flush out a lot of packages that need fixing. But once those fixes start coming in, it's probably going to be straightforward to move forward.

pizza234
5 replies
11h58m

using simple threads instead of dealing with the massive overhead and complexity and bugs of using something like multiprocessing.

Depending on the domain, the reality can be the reverse.

Multiprocessing in the web serving domain, as in "spawning separate processes", is actually simpler and less bug-prone, because there is considerably less resource sharing. The considerably higher difficulty of writing, testing and debugging parallel code is evident to anybody who's worked on it.

As for the overhead, this again depends on the domain. It's hard to quantify, but generalizing to "massive" is not accurate, especially for app servers with COW support.

bausgwi678
3 replies
9h39m

Using multiple processes is simpler in terms of locks etc, but python libraries like multiprocessing or even subprocess.popen[1] which make using multiple processes seem easy are full of footguns which cause deadlocks due to fork-safe code not being well understood. I’ve seen this lead to code ‘working’ and being merged but then triggering sporadic deadlocks in production after a few weeks.

The default for multiprocessing is still to fork (fortunately changing in 3.14), which means all of your parent process’ threaded code (incl. third party libraries) has to be fork-safe. There’s no static analysis checks for this.

This kind of easy to use but incredibly hard to use safely library has made python for long running production services incredibly painful in my experience.

[1] Some arguments to subprocess.popen look handy but actually cause python interpreter code to be executed after the fork and before the execve, which has caused production logging-related deadlocks for me. The original author was very bright but didn’t notice the footgun.

ignoramous
1 replies
1h56m

The default for multiprocessing is still to fork (fortunately changing in 3.14)

If I may: Changing from fork to what?

lyu07282
0 replies
5m

Same experiences, multiprocessing is such a pain in python. It's one of these things people think they can write production code in, but they just haven't run into all the ways their code was wrong so they figure out those bugs later in production.

As an aside I still constantly see side effects in imports in a ton of libraries (up to and including resource allocations).

skissane
0 replies
10h58m

Just the other day I was trying to do two things in parallel in Python using threads - and then I switched to multiprocessing - why? I wanted to immediately terminate one thing whenever the other failed. That’s straightforwardly supported with multiprocessing. With threads, it gets a lot more complicated and can involve things with dubious supportability

wokwokwok
2 replies
10h55m

there is just a tremendous amount of performance that can be automatically unlocked with almost no incremental effort for so many organizations and projects

This just isn’t true.

This does not improve single threaded performance (it’s worse) and concurrent programming is already available.

This will make it less annoying to do concurrent processing.

It also makes everything slower (arguable where that ends up, currently significantly slower) overall.

This way over hyped.

At the end of the day this will be a change that (most likely) makes the existing workloads for everyone slightly slower and makes the lives of a few people a bit easier when they implement natively parallel processing like ML easier and better.

It’s an incremental win for the ML community, and a meaningless/slight loss for everyone else.

At the cost of a great. Deal. Of. Effort.

If you’re excited about it because of the hype and don’t really understand it, probably calm down.

Mostly likely, at the end of the day, it s a change that is totally meaningless to you, won’t really affect you other than making some libraries you use a bit faster, and others a bit slower.

Overall, your standard web application will run a bit slower as a result of it. You probably won’t notice.

Your data stack will run a bit faster. That’s nice.

That’s it.

Over hyped. 100%.

anwlamp
1 replies
7h40m

Yes, good summary. My prediction is that free-threading will be the default at some point because one of the corporations that usurped Python-dev wants it.

The rest of us can live with arcane threading bugs and yet another split ecosystem. As I understand it, if a single C-extension opts for the GIL, the GIL will be enabled.

Of course the invitation to experiment is meaningless. CPython is run by corporations, many excellent developers have left and people will not have any influence on the outcome.

pansa2
0 replies
7h33m

one of the corporations that usurped Python-dev

Man, that phrase perfectly encapsulates so much of Python’s evolution over the last ~10 years.

saurik
1 replies
18h33m

FWIW, I think the concern though is/was that for most of us who aren't doing shared-data multiprocessing this is going to make Python even slower; maybe they figured out how to avoid that?

eigenvalue
0 replies
15h43m

Pretty sure they offset any possible slowdowns by doing heroic optimizations in other parts of CPython. There was even some talk about keeping just those optimizations and leaving the GIL in place, but fortunately they went for the full GILectomy.

Demiurge
1 replies
13h45m

Massive overhead of multiprocessing? How have I not noticed this for tens of years?

I use coroutines and multiprocessing all the time, and saturate every core and all the IO, as needed. I use numpy, pandas, xarray, pytorch, etc.

How did this terrible GIL overhead completely went unnoticed?

viraptor
0 replies
13h37m

I use numpy, pandas, xarray, pytorch, etc.

That means your code is using python as glue and you do most of your work completely outside of cPython. That's why you don't see the impact - those libraries drop GIL when you use them, so there's much less overhead.

quotemstr
0 replies
14h49m

What about the pessimization of single-threaded workloads? I'm still not convinced a completely free-threaded Python is better overall than a multi-interpreter, separate-GIL model with explicit instead of implicit parallelism.

Everyone wants parallelism in Python. Removing the GIL isn't the only way to get it.

quietbritishjim
0 replies
1h19m

If you're worried about performance then much of your CPU time is probably spent in a C extension (e.g. numpy, scipy, opencv, etc.). Those all release the GIL so already allow parallelisation in multiple threads. That even includes many functions in the standard library (e.g. sqlite3, zip/unzip). I've used multiple threads in Python for many years and never needed to break into multiprocessing.

But, for sure, nogil will be good for those workloads written in pure Python (though I've personally never been affected by that).

Galanwe
0 replies
3h52m

It's going to be amazing to saturate all the cores on a big machine using simple threads instead of dealing with the massive overhead and complexity and bugs of using something like multiprocessing.

I'm saturating 192cpu / 1.5TBram machines with no headache and straightforward multiprocessing. I really don't see what multithreading will bring more.

What are these massive overheads / complexity / bugs you're talking about ?

mihaic
7 replies
21h6m

Does anyone know if there is more serious single threaded performance degradation (more than a few percent for instance)? I couldn't find any benchmarks, just some generic reassurance that everything is fine.

ngoldbaum
4 replies
20h58m

Right now there is a significant single-threaded performance cost. Somewhere from 30-50%. Part of what my colleague Ken Jin and others are working on is getting back some of that lost performance by applying some optimizations. Expect single-threaded performance to improve for Python 3.14 next year.

arp242
1 replies
20h2m

To be honest, that seems a lot. Even today a lot of code is single-threaded, and this performance hit will also affect a lot of code running in parallel today.

There have been patches to remove the GIL going back to the 90s and Python 1.5 or thereabouts. But the performance impact has always been the show-stopper.

ngoldbaum
0 replies
19h42m

It’s an experimental release in 3.13. Another example: objects that will have deffered reference counts in 3.14 are made immortal in 3.13 to avoid scaling issues from reference count thrashing. This wasn’t originally the plan but deferred reference counting didn’t land in time for 3.13. It will be several years before free-threading becomes the default, at that point there will no longer be any single-threaded performance drop. Of course that assumes everything shakes out as planned, we’ll see.

This post is a call to ask people to “kick the tires”, experiment, and report issues they run into, not announcing that all work is done.

imtringued
0 replies
15m

That kind of negates the whole purpose of multi threading. An application running on two cores might end up slower, not faster. We know that the python developers are kind of incompetent when it comes to performance, but the numbers you are quoting are so bad they probably aren't correct in the first place.

andmkl
0 replies
18h1m

That would be in the order of previous GIL-removal projects, which were abandoned for that reason.

nhumrich
0 replies
14h8m

Irrelevant, because even if there was, you would use the normal GIL python for it.

deschutes
0 replies
20h55m

To my understanding there is and there isn't. The driving force behind this demonstrated that it was possible to speed up the existing CPython interpreter by more than the performance cost of free threading with changes to the allocator and various other things.

So the net is actually a small performance win but lesser than if there was no free threading. That said, many of the techniques he identified were immediately incorporated into CPython and so I would expect benchmarks to show some regression as compared with the single threaded interpreter of the previous revision.

jmward01
6 replies
18h46m

I know, I know, 'not every story needs to be about ML' but.... I can only imagine how unlocking the GIL will change the nature of ML training and inference. There is so much waste and complexity in passing memory around and coordinating processes. I know that libraries have made it (somewhat) easier and more efficient but I can't wait to see what can be done with things like pytorch when optimized for this.

veber-alex
2 replies
13h28m

huh?

Any python library that cares about performance is written in C/C++/Rust/Fortran and only provides a python interface.

ML will have 0 benefit from this.

jmward01
0 replies
12h58m

Have you done any multi-gpu training? Generally every GPU gets a process. Coordinating between them and passing around data between them is complex and can easily have performance issues since normal communication between python processes requires some sort of serialization/de-serialization of objects (there are many * here when it comes to GPU training). This has the potential to simplify all of that and remove a lot of inter-process communication which is just pure overhead.

KeplerBoy
0 replies
11h45m

Of course ML will benefit from it. Soon you will be able to run your dataloaders/data preprocessing in different threads which will not starve your GPUs of data.

ipsum2
2 replies
17h52m

It'll mostly help for debugging and lowering RAM (not VRAM) usage. Otherwise it won't impact ML much.

jmward01
0 replies
16h29m

Pretty universally I have seen performance improvements in code when complexity is reduced and this could drop complexity considerably. I wouldn't be surprised to see a double digit percent improvement in tokens per sec when an optimized pytorch eventually comes out with this. There may even be hidden gains on GPU memory usage that come out of this as people clean up code and start implementing better tricks because of it.

imtringued
0 replies
11m

Yeah, one of the dumbest things about Dataloaders running in a different process is that you are logging into the void.

grandimam
4 replies
10h36m

How is the no-gil performance compared to other languages like - javascript (nodejs), go, rust, and even java? If it's bearable then I believe there is enormous value that could be generated instead of spending time porting to other languages.

pansa2
3 replies
9h45m

No-GIL Python is still interpreted - single-threaded performance is slower that standard Python, which is in turn much slower than the languages you mentioned.

Maybe if you’ve got an embarrassingly parallel problem, and dozen(s) of cores to spare, you can match the performance of a single-threaded JIT/AOT compiled program.

vulnbludog
2 replies
8h50m

How do companies like Instagram/OpenAI scale with a majority python codebase? Like I just kick it on HN idk much about computers or coding (think high school CS) why wouldn’t they migrate can someone explain like I’m five

pansa2
0 replies
8h24m

Python may well have been the right choice for companies like that when they were starting out, but now they're much bigger, they would be better off with a different language.

However, they simply have too much code to rewrite it all in another language. Hence the attempts recently to fundamentally change Python itself to make it more suitable for large-scale codebases.

<rant>And IMO less suitable for writing small scripts, which is what the majority of Python programmers are actually doing.</rant>

imtringued
0 replies
10m

They have tools like Triton that compile a restricted subset to CUDA.

OutOfHere
4 replies
21h50m

It has been ready for a few months now, at least since 3.13.0 beta 1 which released on 2024-05-08, although alpha versions had it working too. I don't know why this is news now.

With it, the single-threaded case is slower.

TylerE
1 replies
21h48m

FTA: "Yesterday, py-free-threading.github.io launched! It's both a resource with documentation around adding support for free-threaded Python, and a status tracker for the rollout across open source projects in the Python ecosystem."

OutOfHere
0 replies
20h56m

Before the article came the misleading title: "Free-threaded CPython is ready to experiment with".

The link should have been to https://py-free-threading.github.io/tracking/

JBorrow
1 replies
21h31m

This release coincides with the SciPy 2024 conference and a number of other things. I would suggest reading the article to learn more.

OutOfHere
0 replies
20h52m

This release

What release. The last release of CPython was 3.13.0b3 on 2024-06-27.

SciPy is irrelevant to the title.

discreteevent
3 replies
20h31m

I remember back around 2007 all the anxious blog posts about the free lunch (Moore's law) being over. Parallelism was mandatory now. We were going to need exotic solutions like software transactional memory to get out of the crisis (and we could certainly forget about object orientation).

Meanwhile what takes the crown? - Single threaded python.

(Well, ok Rust looks like it's taking first place where you really need the speed and it does help parallelism without requiring absolute purity)

jeremycarter
2 replies
15h40m

Takes what crown? Python is horrifically slow even single threaded. It's by far the slowest and most energy inefficient of the major choices available today.

pansa2
1 replies
15h38m

Popularity

pengaru
0 replies
6h22m

javascript has entered the chat

pansa2
2 replies
17h35m

PEP703 explains that with the GIL removed, operations on lists such as `append` remain thread-safe because of the addition of per-list locks.

What about simple operations like incrementing an integer? IIRC this is currently thread-safe because the GIL guarantees each bytecode instruction is executed atomically.

pansa2
1 replies
17h9m

Ah, `i += 1` isn’t currently thread-safe because Python does (LOAD, +=, STORE) as 3 separate bytecode instructions.

I guess the only things that are a single instruction are some modifications to mutable objects, and those are already heavyweight enough that it’s OK to add a per-object lock.

jillesvangurp
0 replies
13h6m

That sounds like the kind of thing that a JIT compiler should be optimizing. The problem with threading isn't stuff like this but people doing a lot of silly things like having global mutable state or stateful objects that are being passed around a lot.

I've done quite a bit of stuff with Java and Kotlin in the past quarter century and it's interesting to see how much things have evolved. Early on there were a lot of people doing silly things with threads and overusing the, at the time, not so great language features for that. But a lot of that stuff replaced by better primitives and libraries.

If you look at Kotlin these days, there's very little of that silliness going on. It has no synchronized keyword. Or a volatile keyword, like Java has. But it does have co-routines and co-routine scopes. And some of those scopes may be backed by thread pools (or virtual thread pools on recent JVMs).

Now that python has async, it's probably a good idea to start thinking about some way to add structured concurrency similar to that on top of that. So, you have async stuff and some of that async stuff might happen on different threads. It's a good mental model for dealing with concurrency and parallelism. There's no need to repeat two decades of mistakes that happened in the Java world; you can fast forward to the good stuff without doing that.

vegabook
1 replies
11h1m

Clearly the Python 2 to 3 war was so traumatising (and so badly handled) that the core Python team is too scared to do the obvious thing, and call this Python 4.

This is a big fundamental and (in many cases breaking) change, even if it's "optional".

blumomo
0 replies
7h30m

Did Python as the language change which justified that version bump?

vanous
0 replies
13h27m

Thanks for the example and explanations Simon!

elijahbenizzy
1 replies
20h19m

I'm really curious to see how this will work with async. There's a natural barrier (I/O versus CPU-bound code), which isn't always a perfect distinction.

I'd love to see a more fluid model between the two -- E.G. if I'm doing a "gather" on CPU-bound coroutines, I'm curious if there's something that can be smart enough to JIT between async and multithreaded implementations.

"Oh, the first few tasks were entirely CPU-bound? Cool, let's launch another thread. Oh, the first few threads were I/O-bound? Cool, let's use in-thread coroutines".

Probably not feasible for a myriad of reasons, but even a more fluid programming model could be really cool (similar interfaces with a quick swap between?).

bastawhiz
0 replies
18h36m

I think you'd be hard pressed to find a workload where that behavior needs to be generalized to the degree you're talking.

If you're serving HTTP requests, for instance, simply serving each request on its own thread with its own event loop should be sufficient at scale. Multiple requests each with CPU-bound tasks will still saturate the CPUs.

Very little code teeters between CPU-bound and io-bound while also serving few enough requests that you have cores to spare to effectively parallelize all the CPU-bound work. If that's the case, why do you need the runtime to do this for you? A simple profile would show what's holding up the event loop.

But still, the runtime can't naively parallelize coroutines. Coroutines are expected not to be run in parallel and that code isn't expected to be thread safe. Instead of a gather on futures, your code would have been using a thread pool executor in the first place if you'd gone out of your way to ensure your CPU-bound code was thread safe: the benefits of async/await are mostly lost.

I also don't think an event loop can be shared between two running threads: if you were to parallelize coroutines, those coroutines' spawned coroutines could run in parallel. If you used an async library that isn't thread safe because it expects only one coroutine is executing at a time, you could run into serious bugs.

vldmrs
0 replies
20h46m

Great news ! It would be interesting to see performance comparison for IO-bound tasks like http requests between single-threaded asyncio code and multi-threaded asyncio

throwaway5752
0 replies
20h7m

GVR, you are sorely missed, though I hope you are enjoying life.

nas
0 replies
21h54m

Very encouraging news!

gnatolf
0 replies
19h48m

Good to hear. The authors are touching on the journey it is to make Cython continue to work. I wonder how hard it'll be to continue to provide bdist packages, or within what timeframe, if at all, Cython can transparently ensure correctness for a no-gil build. Anyone got any insights?

farhanhubble
0 replies
18h40m

It remains to be seen how many subtle bugs are now introduced by programmers who have never dealt with real multithreading.

earthnail
0 replies
3h14m

Oh how much this would simplify torch.DataLoader (and its equivalents)…

Really excited about this.

anacrolix
0 replies
20h15m

Was ready for this 15 years ago when I loved Python and regularly contributed. At the time, nobody wanted to do it and I got bored and went to Go.