Live data from Hacker News

A viable solution for Python concurrency

lwn.net

61–70 of 366 posts

Re: A viable solution for Python concurrency

#61
post #4

Or you could just use PyPy, which uses a garbage collector, does more compile-time analysis, and runs much faster. CPython is a naive interpreter, like original JavaScript. There's been progress since then.

Pypy is still single threaded. https://doc.pypy.org/en/latest/faq.html#does-pypy-have-a-gil... This work is super exciting! Can pypy use the same recipe to offer true parallelism plus the jit?? Will be really interesting to see what pypy devs think of this work and how they might also lever it!

I think it can't use the same recipe. Sam's approach for CPython uses biased reference counting. Internally, Pypy uses a tracing garbage collector, not reference counting. I don't know how difficult it would be to make their GC thread-safe. Probably you don't want to "stop the world" on every GC pass so I guess changes are non-trivial.

Sam's changes to CPython's container objects (dicts, lists), to make them thread safe might also be hard to port directly to Pypy. Pypy implements those objects differently.

Re: A viable solution for Python concurrency

#62
post #19

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.

What specifically is the problem with asyncio? I quite like using it, so I'm curious if there's some aspect that makes it unsustainable?

> What specifically is the problem with asyncio?

Watch the very NSFW (lots of swearing) but hysterically funny video "node.js is bad ass rock star tech" on youtube sometime ;). https://www.youtube.com/watch?v=bzkRVzciAZg

Re: A viable solution for Python concurrency

#63
post #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…

Good point. Windows COM did not follow your suggestion, leading to all sorts of awkwardness in applications that have compute- and ui-threads and share objects between the two. Object destruction becomes non-predictable and can hold up a UI thread.

Re: A viable solution for Python concurrency

#64

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.

asyncio is not a competition to threads, it's complementary. In fact, it's a perfectly viable strat in python to have several processes, each having several threads, each having an event loop. And it will still be so, once this comes out. You will certainly use threads more, and processes less, but replacing 1000000 coroutines by 1000000 system threads is not necessarily the right strategy for your task. See nginx vs…

"Viable" as in "you have no other choice sometimes". This forces you to deal with 3 libraries each with their own quirks, pitfalls and incompatibilities. Sometimes you even deal with dependencies reimplementing some parts in a 4th or 5th library to deal with shortcomings.

I really don't care that much which of them survive, I just want to rely on less of them

Re: A viable solution for Python concurrency

#65

Earlier quoted context omitted.

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.

Most objects are never shared so there would be a performance impact from incrementing (and decrementing) an atomic counter even just once.

That is true, but what if the shared count were initialized to one and the creator thread frees an object when the shared count is equal to one and the local count is decremented zero? (Since it knows it holds one shared reference.) Then the increment and decrement would be avoided for non-shared objects.

Re: A viable solution for Python concurrency

#66
This is a great list of influences on the design (from the article comments where the prototype author Sam Gross responded to someone wishing for more cross pollination across language communities):

—————

"… but I'll give a few more examples specific to this project of ideas (or code) taken from other communities:

- Biased reference counting (originally implemented for Swift)

- mimalloc (originally developed for Koka and Lean)

- The design of the internal locks is taken from WebKit (https://webkit.org/blog/6161/locking-in-webkit/)

- The collection thread-safety adapts some code from FreeBSD (https://github.com/colesbury/nogil/blob/nogil/Python/qsbr.c)

- The interpreter took ideas from LuaJIT and V8's ignition interpreter (the register-accumulator model from ignition, fast function calls and other perf ideas from LuaJIT)

- The stop-the-world implementation is influenced by Go's design (https://github.com/golang/go/blob/fad4a16fd43f6a72b6917eff65... )"

Re: A viable solution for Python concurrency

#67
post #41

Earlier quoted context omitted.

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.

Because JavaScript never had threads so I/O in JavaScript has always been non-blocking and the whole ecosystem surrounding it has grown up under that assumption. JavaScript doesn't need a GIL because it doesn't really have threads. WebWorkers are more akin to multiprocessing than threads in Python. Objects cannot be shared directly across WebWorkers so transferring data comes with the expense of serializing/deseriali…

JS now has shared array buffers.

Re: A viable solution for Python concurrency

#68
post #61

Earlier quoted context omitted.

Pypy is still single threaded. https://doc.pypy.org/en/latest/faq.html#does-pypy-have-a-gil... This work is super exciting! Can pypy use the same recipe to offer true parallelism plus the jit?? Will be really interesting to see what pypy devs think of this work and how they might also lever it!

I think it can't use the same recipe. Sam's approach for CPython uses biased reference counting. Internally, Pypy uses a tracing garbage collector, not reference counting. I don't know how difficult it would be to make their GC thread-safe. Probably you don't want to "stop the world" on every GC pass so I guess changes are non-trivial. Sam's changes to CPython's container objects (dicts, lists), to make them thread s…

I think the biggest thing it will give is a need to go there. Until now, pypy has been able to not do parallelism. But if cpython is suddenly faster for a big class of program, pypy will have to bite the bullet to stay relevant?

Re: A viable solution for Python concurrency

#69
post #4

Or you could just use PyPy, which uses a garbage collector, does more compile-time analysis, and runs much faster. CPython is a naive interpreter, like original JavaScript. There's been progress since then.

It's been a few years since I last played around with PyPy but while it provided amazing performance gains for simple algorithmic code I saw no speed up on a more complex web application.

Re: A viable solution for Python concurrency

#70
post #57

Earlier quoted context omitted.

asyncio is not a competition to threads, it's complementary. In fact, it's a perfectly viable strat in python to have several processes, each having several threads, each having an event loop. And it will still be so, once this comes out. You will certainly use threads more, and processes less, but replacing 1000000 coroutines by 1000000 system threads is not necessarily the right strategy for your task. See nginx vs…

Multiple threads with one asyncio loop per thread would be absolutely pointless in Python, because of the GIL. With that said, sure, threads and asyncio are complimentary in the sense that you can run tasks on threadpool executors and treat them as if they were coroutines on an event loop. But that serves no purpose unless you're trying to do blocking IO without blocking your whole process.

In Python it would be pointless, but for example it's how Seastar/ScyllaDB work: each thread is bound to a CPU on the host and has its own reactor (event loop) with coroutines on it. QEMU has a similar design.
Post reply on HN