Live data from Hacker News

A viable solution for Python concurrency

lwn.net

41–50 of 366 posts

Re: A viable solution for Python concurrency

#41

If this effort succeeds (and I hope it does) now Python developers will need to contend with the event-loop albatross of asyncio and all of its weird complexity. In an alternate Python timeline, asyncio was not introduced into the Python standard library, and instead we got a natively supported, robust, easy-to-use concurrency paradigm built around green/virtual threading that accommodates both IO and CPU bound work.

BTW I wonder why async is so painless in ES6 compared to Python. Why the presence of GIL (which JS also has) did not make running async coroutines completely transparent, as it made running generators (which are, well, coroutines already). Why the whole even loop thing is even visible at all.

Re: A viable solution for Python concurrency

#42

If this effort succeeds (and I hope it does) now Python developers will need to contend with the event-loop albatross of asyncio and all of its weird complexity. In an alternate Python timeline, asyncio was not introduced into the Python standard library, and instead we got a natively supported, robust, easy-to-use concurrency paradigm built around green/virtual threading that accommodates both IO and CPU bound work.

so true. I've been writing thread-callback code for decades (common in network and gui event loops, see QtPy as an example) and when I looked at asyncio my first thought is "this is not better". It's entirely nontrivial to analyze code using asyncio (or yield) compared to callbacks.

Re: A viable solution for Python concurrency

#43
post #5

Earlier quoted context omitted.

Yes. I once discovered that CPickle was not thread-safe. The response was that much of the library didn't really work in multi-threaded programs.

You mean programs where you put an object into pickle and some other threads modify it while pickle is processing it? Doesn't surprise me - the equivalent written in plain Python would be very thread unsafe as well.

No, I mean several threads doing completely separate CPickle streams with no shared data or variables at the Python level.

Re: A viable solution for Python concurrency

#44

> With this scheme, the reference count in each object is split in two, with one "local" count for the owner (creator) of the object and a shared count for all other threads. Since the owner has exclusive access to its count, increments and decrements can be done with fast, non-atomic instructions. Any other thread accessing the object will use atomic operations on the shared reference count. > Whenever the owning th…

I like this idea. In fact another possibility is to have a thread-local reference count for each thread that uses the object which can use fast non-atomic operations, and then each thread can use a shared atomic reference count, that counts how many threads use the object. When each thread-local count goes to zero, the shared count is decremented by one.

This way, if an object is created in one thread and transferred to another, the other thread wouldn't even need to do a lot of atomic reference count manipulations. There wouldn't be surprising behavior in which different threads run the same code with different speed, just by virtue of whether they created the objects or not.

Re: A viable solution for Python concurrency

#45
post #2

> This "optimization" actually slows single-threaded accesses down slightly, according to the design document, but that penalty becomes worthwhile once multi-threaded execution becomes possible. My understanding was that CPython viewed any single-threaded performance regression as a blocker to GIL-removal attempts, regardless of if other work by the developer has sped up the interpreter? This article seems to somewha…

There are people who believe all kinds of crazy things; it doesn't reflect their truth. Going back to Python 2 is not going to ever happen (and no one working on Py3 would ever want to, anyway).

A hard pill to swallow.. ain't that bad if it also benefits you tremendously, which fixing the GIL would do.

Re: A viable solution for Python concurrency

#46

> With this scheme, the reference count in each object is split in two, with one "local" count for the owner (creator) of the object and a shared count for all other threads. Since the owner has exclusive access to its count, increments and decrements can be done with fast, non-atomic instructions. Any other thread accessing the object will use atomic operations on the shared reference count. > Whenever the owning th…

I think that idea was mentioned earlier in the article:

> The simplest change would be to replace non-atomic reference count operations with their atomic equivalents. However, atomic instructions are more expensive than their non-atomic counterparts. Replacing Py_INCREF and Py_DECREF with atomic variants would result in a 60% average slowdown on the pyperformance benchmark suite.

Re: A viable solution for Python concurrency

#47

> With this scheme, the reference count in each object is split in two, with one "local" count for the owner (creator) of the object and a shared count for all other threads. Since the owner has exclusive access to its count, increments and decrements can be done with fast, non-atomic instructions. Any other thread accessing the object will use atomic operations on the shared reference count. > Whenever the owning th…

Yeah, I'm not exactly getting all the complexity here.

I'm digging the 2 reference counters, that makes sense to me, but I don't know why it isn't something more like:

"every time a new thread takes a reference, atomic +1, every time a new thread's local count hits 0, atomic -1. If the shared reference is 0, free".

IDK what special purpose the flags are serving here.

Re: A viable solution for Python concurrency

#48
> "biased reference counts" and is described in this paper by Jiho Choi et al. With this scheme, the reference count in each object is split in two, with one "local" count for the owner (creator) of the object and a shared count for all other threads

> The interpreter's memory allocator has been replaced with mimalloc

These are very similar ideas!

Mimalloc is notable for its use of separate local and remote free lists, where objects that are being freed from a different thread than the page’s heap’s owner are placed in a separate queue. The local free list is (IIRC) non-atomic until it is empty and local allocs start pulling from the remote queue.

The general idea is clearly lazy support for concurrency, matching up perfectly with Python’s need to keep any single threaded perf it has. I’m impressed with the application of all of these things at once.

Re: A viable solution for Python concurrency

#49

Earlier quoted context omitted.

There are very few new features in 3.8. It is a much less important release (for features) than 3.7, which for example added dataclasses and lots of typing and asyncio stuff. The most significant change in 3.8 is a notoriously controversial new infix operator. Even it's supporters would say that it's a niche usecase.

> There are very few new features in 3.8. > It is a much less important release (for features) than 3.7, which for example added dataclasses and lots of typing and asyncio stuff. That's funny because my take is the exact opposite: dataclasses are not very useful (attrs exists and does more), deferred type annotations are meh, contextvars, breakpoint(), and module-level getattr/settattr but not exactly anything you ca…

Deferred type annotations with `from __future__ import annotations` are a game-changer IMO. You can use them 3.7, which is good enough for me. The big improvement in 3.9 is not having to use `typing.*` for a lot of basic data types.

The biggest improvements between 3.7, 3.8, 3.9, and 3.10 are in `asyncio`, which was pretty rough in 3.7 and very usable in 3.9. I use the 3rd-party `anyio` library in a lot of cases anyway (https://anyio.readthedocs.io/), but it's not always feasible.

Re: A viable solution for Python concurrency

#50

I feel like Gvr just doesnt want to change things, Feels doomed This has been a problem for like 20 years and they have refused fixes before. And there have been fixes. They just don't see this as important it's practically a religion that its a thing they wont change

Guido is no longer the BDF and spoke fairly positively about this change in the mailing list thread[1].

"To be clear, Sam’s basic approach is a bit slower for single-threaded code, and he admits that. But to sweeten the pot he has also applied a bunch of unrelated speedups that make it faster in general, so that overall it’s always a win. But presumably we could upstream the latter easily, separately from the GIL-freeing part."

[1] https://mail.python.org/archives/list/python-dev@python.org/...

Post reply on HN