Live data from Hacker News

How to choose the right Python concurrency API

superfastpython.com

51–60 of 64 posts

Re: How to choose the right Python concurrency API

#51
post #8

I kind of wish we'd just add Go-style CSP to python and call it a day - it seems to be the best of both worlds when it comes "do two things at the same time".

I'm not sure how much it replicates the CSP model, but the closest thing I've found to Go-style concurrency in Python is gevent: https://github.com/gevent/gevent I personally still prefer to use it in all my projects.

I do, too, for websites, although I try to avoid dependencies like a snowflake the fire. But doing nothing more than using gevent server and putting

  from gevent import monkey
  monkey.patch_all()
at the top of the main module was such a game changer.

Re: How to choose the right Python concurrency API

#52
post #50

I am not sure when this article was written, but I really wouldn't recommend threads for even IO-bound tasks. Since threads don't escape the GIL the added contention with OS thread scheduling (context switching) is a performance overhead that coroutines (asyncio) don't have. I haven't benchmarked threads vs. coroutines for IO-bound tasks...but my gut feeling is coroutines are going to generally be better because of t…

But threads do release GIL on IO. There's a slight overhead of course, but if you're not running hundreds of servers and thousands of threads, the overhead can be negligible. I've seen more than once people rewriting thread-based code to coroutines, despite not having any noticeable benefits other than being more sexy. In fact I was one of those people almost a decade ago, rewriting the whole networking portion of th…

I am not expert, and this might be dated information, but historically there is a "thrashing" introduced between the GIL and OS scheduled threads which has surprising performance implications.

David Beazley does a much better illustrating the nuances than I ever could:

https://www.youtube.com/watch?v=Obt-vMVdM8s

Including a wild phenomenon where a threaded Python program's performance can degrade by increasing number of CPU cores available to a system.

Coroutines avoid this by not requiring individual thread scheduling by the OS. So they effectively release the GIL like threads on IO but avoid OS level thread switching.

Re: How to choose the right Python concurrency API

#53
post #50

Earlier quoted context omitted.

But threads do release GIL on IO. There's a slight overhead of course, but if you're not running hundreds of servers and thousands of threads, the overhead can be negligible. I've seen more than once people rewriting thread-based code to coroutines, despite not having any noticeable benefits other than being more sexy. In fact I was one of those people almost a decade ago, rewriting the whole networking portion of th…

I am not expert, and this might be dated information, but historically there is a "thrashing" introduced between the GIL and OS scheduled threads which has surprising performance implications. David Beazley does a much better illustrating the nuances than I ever could: https://www.youtube.com/watch?v=Obt-vMVdM8s Including a wild phenomenon where a threaded Python program's performance can degrade by increasing number…

It is a bit dated (he talks about GIL improvements in 3.2), but regardless I think it only proves my point. The examples David shows are threads with zero IO unable to properly cooperate. Having threads that are mostly doing IO would actually allow them to cooperate natively by releasing GIL.

Another interesting example he talks about is IO-bound threads competing with CPU-bound threads, where CPU-threads are getting seemingly unfair advantage. But the same example would only get worse with coroutine implementation: since there's only one thread, CPU bound code would just block IO forever. IO-bound code would not just be slow, it would never execute.

What David illustrated is that GIL makes it harder to mix CPU- and IO-bound tasks in the same process. But coroutines are not the solution here. Solution is not to mix them.

Re: How to choose the right Python concurrency API

#54

If Python is slow and you need concurrency, and you have a long-ish timeframe in mind, do yourself a favor and use one of C++/Java/Rust/Go/Erlang/Elixir/(other newer programming language). Especially, don’t rely on Multiprocessing. I’ve been developing and maintaining a largish batch processing Python project where performance is a key feature, and it’s been very frustrating. I should have rewritten it when I had the…

Genuinely asking, why don't you add dotnet ? I'm currently in the situation you describe and I would like to try something new. Dotnet is among my candidates. (only asking for personal projects)

Mostly because I forgot it.

Re: How to choose the right Python concurrency API

#55
post #40

If Python is slow and you need concurrency, and you have a long-ish timeframe in mind, do yourself a favor and use one of C++/Java/Rust/Go/Erlang/Elixir/(other newer programming language). Especially, don’t rely on Multiprocessing. I’ve been developing and maintaining a largish batch processing Python project where performance is a key feature, and it’s been very frustrating. I should have rewritten it when I had the…

[dead]

Scaling horizontally is well and good (since my unit of input is large and I get a lot of them, I just use autoscaling groups in AWS to achieve much the same), but I don’t want to pay for 10 systems slowly doing what 1 could do with fast software. Especially, while 1 system has a hard ceiling, there’s a lot of headroom on 1 system when you achieve mechanical sympathy with the cache hierarchy, memory controller, and disk.

Re: How to choose the right Python concurrency API

#56

If Python is slow and you need concurrency, and you have a long-ish timeframe in mind, do yourself a favor and use one of C++/Java/Rust/Go/Erlang/Elixir/(other newer programming language). Especially, don’t rely on Multiprocessing. I’ve been developing and maintaining a largish batch processing Python project where performance is a key feature, and it’s been very frustrating. I should have rewritten it when I had the…

I think I need Python concurrency because I'm using it to talk to a bunch of hardware in parallel (using pexpect, for testing), collecting global statistics. I'm not using concurrency for performance (well, not in the usual way I guess). Using a different language would put me at odds with the rest of the team, increasing the maintenance burden. What solution would people recommend?

That seems like a better application for Python concurrency than trying to eke out more performance.

I don’t particularly like Go, but it might be worth a look for this since it’s somewhat easier to learn and may be more accessible for others to learn and maintain. I wouldn’t do a full implementation, just an exploratory project for the sake of seeing whether you’re stuck on a local maxima with Python.

Re: How to choose the right Python concurrency API

#57

The article gives a good summary of the quite complex landscape of concurrency in python. There's more to it, for example gil-free c-extensions, subprocesses and cross-machine (plus IPC) communication. But I'm particularly bothered by the fact that many articles and tutorials look at concurrency as if it's only about factoring primes or writing a web server with many (perhaps even idempotent) parallel requests. In re…

Python does have an actor framework: thespian. It’s not bad, but not Erlang either.

Re: How to choose the right Python concurrency API

#58
post #50

I am not sure when this article was written, but I really wouldn't recommend threads for even IO-bound tasks. Since threads don't escape the GIL the added contention with OS thread scheduling (context switching) is a performance overhead that coroutines (asyncio) don't have. I haven't benchmarked threads vs. coroutines for IO-bound tasks...but my gut feeling is coroutines are going to generally be better because of t…

But threads do release GIL on IO. There's a slight overhead of course, but if you're not running hundreds of servers and thousands of threads, the overhead can be negligible. I've seen more than once people rewriting thread-based code to coroutines, despite not having any noticeable benefits other than being more sexy. In fact I was one of those people almost a decade ago, rewriting the whole networking portion of th…

5% performance increase is worthwhile though?

Re: How to choose the right Python concurrency API

#59
post #53

Earlier quoted context omitted.

I am not expert, and this might be dated information, but historically there is a "thrashing" introduced between the GIL and OS scheduled threads which has surprising performance implications. David Beazley does a much better illustrating the nuances than I ever could: https://www.youtube.com/watch?v=Obt-vMVdM8s Including a wild phenomenon where a threaded Python program's performance can degrade by increasing number…

It is a bit dated (he talks about GIL improvements in 3.2), but regardless I think it only proves my point. The examples David shows are threads with zero IO unable to properly cooperate. Having threads that are mostly doing IO would actually allow them to cooperate natively by releasing GIL. Another interesting example he talks about is IO-bound threads competing with CPU-bound threads, where CPU-threads are getting…

Right so the GIL improvements which (probably?) made it into 3.2 solves thrashing but introduces latency during "negotiation" for the GIL.

> What David illustrated is that GIL makes it harder to mix CPU- and IO-bound tasks in the same process. But coroutines are not the solution here. Solution is not to mix them.

Which I believe is my point and why I forwarded the personal notion that there really is only two acceptable concurrency models in Python. Coroutines solve cooperative scheduling for things that can be cooperatively scheduled. Threads _could_, but in Python specifically threads are strictly inferior as they have additional overhead by nature. In slightly different phrasing, Threads can be nearly or even acceptably equivalent to coroutines in performance, but they will never be better so long as Python has a GIL.

For everything else there is Multiprocessing (for example segmenting CPU processing from IO over separate processes).

Re: How to choose the right Python concurrency API

#60
post #58
post #50

Earlier quoted context omitted.

But threads do release GIL on IO. There's a slight overhead of course, but if you're not running hundreds of servers and thousands of threads, the overhead can be negligible. I've seen more than once people rewriting thread-based code to coroutines, despite not having any noticeable benefits other than being more sexy. In fact I was one of those people almost a decade ago, rewriting the whole networking portion of th…

5% performance increase is worthwhile though?

Only if it's free. The costs were quite high:

Time spend rewriting networking

Code because more complicated: cooperation was moved to code instead of being handled by OS (to be fair this was way before asyncio, it was gevent).

Code became alien to the team. I was the only one who knew how it worked.

In the end, the only problem I solved was the problem we didn't have: "not using coroutines". Sure, threads suck at scale. But you should build things for scale when it's necessary, not when it's fun.

Post reply on HN