Gunicorn: gevent vs gevent_wsgi vs gevent_pywsgi workers

Created on 25 Feb 2012  路  6Comments  路  Source: benoitc/gunicorn

What's the relation of the (apparently undocumented) gevent_wsgi and gevent_pywsgi to the "plain" gevent worker type?

Most helpful comment

A basic hello world application is unlikely to see any performance improvement from gevent, especially if the benchmark is run locally or on a high performance network. The reason for this is that gevent only switches greenlets when performing I/O operations that would block. If you can receive the entire HTTP request in a single recv() call and respond with a single send() call, you are more or less doing what the sync worker is doing. The benefit of gevent is when your application has to wait on slow connections or upstream connections (like connections to a database or external API). Using gevent lets your application handle other requests at the same time it's waiting for those operations.

With gevent worker type we use the gevent.server.StreamServer to create a TCP server. That handles the transport layer, but we still need to parse the stream for HTTP requests, create the WSGI environment, call the WSGI application with the environment, and serialize an HTTP response.

The Gunicorn parser code is in message.py.

With the gevent_pywsgi worker, the parsing logic from gevent can be found in pywsgi.py.

All 6 comments

gevent worker is our own worker using our wsgi parser, while otheres are gevent parsers.

Sorry to bump a 7 year old issue, but I was recently stuck with this same question for about a week.

I think that better documentation should be made for these 3 worker types.

Short summary of the week: I was experiencing identical performance for the sync and gevent workers, which was really confounding me. After looking at the source code, I discovered the gevent_pywsgi type which dramatically increased my performance.

Just curious, what is the purpose of the gevent worker option?

gevent_pywsgi and gevent_wsgi are the same. Names provided for backward compatibility. We could deprecate and remove one to make it slightly less confusing.

The rest is as described in the comment above. The regular gevent uses our parser code, while _wsgi uses one from Gevent.

If you find that gevent_pywsgi increases performance, that would not be a surprise. However, if you find identical performance between sync and gevent then it seems like you are not getting much benefit from gevent itself and therefore the only speed you get from gevent_pywsgi is from the parsing code.

You may want to try the gevent worker until you figure out the bottleneck, and whether you need asynchronous workers at all. Also, maybe you can say more about what you mean by performance: throughput (requests per second), latency (minimum), latency (average), etc. The sync and gevent workers _are_ different.

Just curious, what is the purpose of the gevent worker option?

To patch the blocking I/O functions of the standard library to use the gevent hub and handle concurrent requests in greenlets. If you need to handle a high volume of concurrent requests and your application performs a lot of waiting on I/O (database, streaming responses, upstream requests, etc) then gevent can be a good choice.

@tilgovi here is a recent stack overflow I made regarding this issue

I was looking to improve throughput on a basic Flask hello world, and could not get above 125RPS with either the sync or gevent worker type.

Could you clarify the following for me? I'm still a bit confused.:

The regular gevent uses our parser code

What do you mean by parser?

In addition, looking at the source code of Gunicorn it seems that with -k gevent, the gevent.server.StreamServer is type is used, and with -k gevent_pywsgi, the gevent.pywsgi.WSGIServer type is used.

A basic hello world application is unlikely to see any performance improvement from gevent, especially if the benchmark is run locally or on a high performance network. The reason for this is that gevent only switches greenlets when performing I/O operations that would block. If you can receive the entire HTTP request in a single recv() call and respond with a single send() call, you are more or less doing what the sync worker is doing. The benefit of gevent is when your application has to wait on slow connections or upstream connections (like connections to a database or external API). Using gevent lets your application handle other requests at the same time it's waiting for those operations.

With gevent worker type we use the gevent.server.StreamServer to create a TCP server. That handles the transport layer, but we still need to parse the stream for HTTP requests, create the WSGI environment, call the WSGI application with the environment, and serialize an HTTP response.

The Gunicorn parser code is in message.py.

With the gevent_pywsgi worker, the parsing logic from gevent can be found in pywsgi.py.

Thank you for the through explanations @tilgovi , its much appreciated!

Was this page helpful?
0 / 5 - 0 ratings