Skip to content

Instantly share code, notes, and snippets.

What would you like to do?

Performance of Flask, Tornado, GEvent, and their combinations

Wensheng Wang, 10/1/11


When choosing a web framework, I pretty much have eyes set on Tornado. But I heard good things about Flask and Gevent. So I tested the performance of each and combinations of the three. I chose something just a little more advanced than a "Hello World" program to write - one that use templates. Here are the codes:

1, Pure Flask (

from flask import Flask, render_template
app = Flask(__name__)

def main_handler():
   return render_template('main_j2.html', messages="whatever",title="home")

if __name__ == '__main__':, debug=False)

2, Pure Tornado (

import os.path
import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
   def get(self):
      self.render('main.html', page_title="", body_id="", messages="whatever",title="home")

settings = {
application = tornado.web.Application([
   (r"/", MainHandler),
], **settings)

if __name__ == "__main__":

3, Flask with Gevent (

from gevent.wsgi import WSGIServer
from pure_flask import app

http_server = WSGIServer(('', 8888), app)

4, Flask with Tornado (

from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from pure_flask import app

http_server = HTTPServer(WSGIContainer(app))

5, Tornado with Gevent (

import tornado.wsgi
import gevent.wsgi
import pure_tornado

application = tornado.wsgi.WSGIApplication([
   (r"/", pure_tornado.MainHandler),

if __name__ == "__main__":
   server = gevent.wsgi.WSGIServer(('', 8888), application)

I have 3 template files: main.html, layout.html, and form.html. main.html "extends" layout.html and "includes" form.html. The total size of templates is about 30kB.

The reason Flask and Tornado use different templates (main.html and main_j2.html) is that their template syntax is slightly different. Flask (jinja2) template use "{% endblock %}", "{% endfor %}", etc. while Tornado just use "{% end %}".

I tested performance (requests per second) using ApacheBench:

ab -n 1000 -c 4 http://localhost:8888/

and run it 5 times. The testing is done on a 6-year old dual-Opteron 254 server.

Here are the results:

pure_flask:      82  88 107 102  71
pure_tornado:   144 244 241 294 290
gevent_flask:   127 139 145 152 110
tornado_flask:  110  88  74  92 101
gevent_tornado: 328 555 177 273 153

Here are the averages:

pure_flask:      90
pure_tornado:   242
gevent_flask:   135
tornado_flask:   93
gevent_tornado: 297

As you can see, the Tornado implementation is significantly faster than Flask. Gevent makes Tornado faster, but not by a lot.

In the end, I like the straightforward style of Tornado and not the Flask way to write large project (using blueprints), So I sticks with Tornado.

Copy link

tkisme commented Feb 2, 2016

add compare of gunicron

Copy link

stdex commented Aug 11, 2016

What versions you use?

Copy link

badkk commented Sep 13, 2017

add gunicorn +1

Copy link

dkavraal commented Sep 29, 2017

gunicorn +1

and thanks for the effort

Copy link

jkryanchou commented Oct 31, 2017

gunicorn +1

Copy link

isaccavalcante commented Dec 31, 2017

gunicorn +1

Copy link

PegasusWang commented Jan 22, 2018

add database query

Copy link

josejaguirre commented Mar 4, 2018

If I use tornado with flask, will blocking requests from flask be non-blocking?

Copy link

krinkere commented Jun 21, 2018

gunicorn +1

Copy link

DannysLiu commented Sep 11, 2018

gunicorn +1

Copy link

AntmanTmp commented Sep 25, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment