3 ms·
Most of our stack is built around gevent, it works great just about everywhere and requires little to no thought about the asynchronous nature of the code. Perf
by jacob019 9y ago
Most of our stack is built around gevent, it works great just about everywhere and requires little to no thought about the asynchronous nature of the code. Performance has been incredible compared to the old thread based code it replaced. Asyncio seems nice in theory, being baked in and all, but we tried it with some sample apps and it just requires too much thought to stay asynchronous, it's a takes a total paradigm shift like twisted. It's easy to slip up and have parts of the code become synchronous and the code gets hairy. We decided to just stick with what we know.
- WaxProlix 9y agoGevent is just a little too good at what it does; the fact that it's performant and lightweight and easy to pick up and start moving on but still scales well... just makes it hard for asyncio to disrupt, especially when greenlets beat them to the table by a pretty huge margin.
- deathanatos 9y agoI'm assuming your stack looks like mine: gevent has monkey patched everything. 1. fork'ing just doesn't work properly; something about the event loop in the child hangs. 2. You can't easily spawn an honest to god hardware thread anymore. (Since the typical call is monkey patched!) Yes, you can get the original threading module by getting it out of gevents hands, but… 3. …it doesn't compose well. Libraries, in order for gevent to play nice, need special "if gevent then …" sections.
- jacob019 9y agoNo need for forking or threads, that's the point of async. Yeah there's limitations when CPU bound, but that's python. Fire up a few processes and load balance. #3 hasn't been an issue for us, monkey patching works well. We've switched to pure python versions of a few libraries. When not available use a pool of instances.
- sandGorgon 9y agoThe problem is with some specific features like select.epoll. gevent doesn't support that and will break such code. This includes socketio libraries - we use third party tools like pusher.com and all of them break with python 3. E.g. https://github.com/benoitc/gunicorn/issues/1494 https://github.com/benoitc/gunicorn/issues/1494 Gevent explicitly says : "Given that epoll is more sophisticated than select or poll (e.g., support for edge triggered and level triggered events), I don't know if we can emulate that close enough with the capabilities that libev exposes (we'd need to pass the stdlib tests when monkey-patched), and I don't know what the performance characteristics would be (and if the subtle differences would break apps) " I don't see a path forward for gevent with these issues in place.
- sametmax 9y agoOf course you need threads even with async. When you code blocks for CPU reasons, like handling a heavy HTTP request with lots of calculations, you don't want to stop serving other requests.
- jacob019 9y agoIt depends. If the calculation can be split into nice chunks you can throw in some gevent.sleep(0) to prevent them from hogging the process. We use python for handling web requests, anything heavy tends to be handed off to another process. It's rare for us but yeah, sometimes you need threads.
- deathanatos 9y ago> No need for forking … Fire up a few processes First, forking is "firing up a few processes". Yes, you can do that prior to launching your process, and then let gevent just have the process, but you have to be able to do that, and it just doesn't make sense in all contexts. This is what I meant by gevent fights composability. > or threads There's a limit to what a single CPU can do, and eventually multiple hardware threads can buy you actual tangible results. > Yeah there's limitations when CPU bound, but that's python. My point being that it's very difficult to get to CPU bound when your greenlet library prevents you from taking advantage of threads or processes. "but that's python" implies to me that you think Python is hamstrung to a single thread, and that's not the case: Python is quite capable of not only hardware threads, but performing computation on hardware threads in parallel, simply because not all of my code is in Python. Sometimes, a component knows it needs an honest-to-god HW thread, and the monkey patching fights that. HW threads additionally lets you run multiple-long running computations; even if they're mutually bound to a single CPU, preemptive multitasking at the OS level (and Python's interpreter occasionally releasing the GIL) will ensure they all get time. In a cooperative greenlet, if you do not yield, you starve the other threads. A library making a long running computation again needs intimate knowledge that at the higher level, greenlets are in use: this breaks layers and mixes responsibilities (greenlets) in where it doesn't belong (some library calculating a thing). Greenlets do not compose. > We've switched to pure python versions of a few libraries. And you pretty much have to; if the library called out to C, it would block. But this fights good engineering practices: you can't just implement your library once in the language of choice, and then bind it into another language. > When not available use a pool of instances. This depends on what the library does; a "pool" does not necessarily save you. For example, if the library blocks, you need a threadpool (a real one) to process inputs to it. Libraries exposing an event loop, perhaps with some basic primitives, have a hope of communicating across things like language barriers.
- rdtsc 9y agoYes looking back I think asyncio was a blunder. I've been using Python for more than 12 years now. Used threads, then Twisted, then eventlet, then gevent. Switching to Twisted was hard but oh well, there was nothing else that did the job right for lots of concurrent requests. Unfortunately it also created a separate library system, fragmented and cumbersome. You'd want a library, you find it "oh but it doesn't return deferreds it blocks the current thread". Or vice versa when using threads, I'd find the perfect library to parse my protocol but it returned deferreds only. Then eventlet and gevent came about it was like a breath of fresh air. I remember refactoring a largish code base from Twisted to eventlet and it was something like a 50% reduction in code size. Then gevent came about with more improvements and so on. Then async came, and now it's like back to Twisted. Find a library but well, it return the async thing not a result, so it has to live in a separate ecosystem. You have write async this async that everywhere. Also, maybe if async came 5-7 years ago it would have taken over but now once people have to rewrite everything with async they'd also start looking at other languages: like Elixir and Go which somehow managed to avoid "async everywhere" and still do the right thing. Python almost had that too with greenlet based libraries. Embracing that would have probably made more sense, but oh well...