It's the season of ISUCON. ISUCON customarily uses a typical micro-framework for each language, but Python has been using Flask for now.
Flask certainly looks like a micro-framework when writing a simple sample app. However, structurally there are many hooks and signals, and it is a heavyweight design.
The combined Flask body and Werkzeug are tens of thousands of lines in size. Even a mere Hello World app has dozens of function calls behind the scenes.
Like Flask, Bottle is a framework that is multi-threaded, has a context stack that uses thread locals, and has extensions, but its structure is much simpler than Flask. The source code is 3000 lines per file, and the overhead of the framework is about half that of Flask.
I made a small measurement with the Hello app. Benchmark a single-threaded, single-process Hello app on a MacBook Air 2013 Mid (Core i5 1.3GHz) with wrk -t1 -c1. Python is CPython 3.4.1 and the web server is Meinheld (latest version on Github).
wsgi:
$ wrk -t1 -c1 http://localhost:6000/
Running 10s test @ http://localhost:6000/
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 118.89us 46.59us 0.99ms 91.40%
Req/Sec 7.57k 622.67 9.90k 67.44%
71708 requests in 10.00s, 11.08MB read
Requests/sec: 7170.95
Transfer/sec: 1.11MB
Bottle:
$ wrk -t1 -c1 http://localhost:6000/
Running 10s test @ http://localhost:6000/
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 221.92us 678.78us 18.72ms 99.81%
Req/Sec 4.86k 418.08 6.44k 78.08%
46110 requests in 10.00s, 7.74MB read
Requests/sec: 4611.09
Transfer/sec: 792.53KB
Flask:
$ wrk -t1 -c1 http://localhost:6000/
Running 10s test @ http://localhost:6000/
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 476.59us 134.74us 1.71ms 79.35%
Req/Sec 2.11k 194.96 3.10k 65.86%
19967 requests in 10.00s, 3.35MB read
Requests/sec: 1996.71
Transfer/sec: 343.18KB
Looking at the difference in Avg Latency, about 100 μs for Bottle and about 350 μs for Flask are overheads for raw wsgi, which is more than double the total performance.
Bottle also supports Jinja templates, so even if your initial app was Flask, you should be able to switch to Bottle reasonably easily. In cases where you're hitting a lot of paths that just cache the HTML part and stick the data from Memcached back together, the performance of this base part should be a non-negligible difference.
Lastly, I will put the measured source code. Please try it.
import flask
import bottle
app = flask.Flask(__name__)
bottle_app = bottle.app()
@bottle_app.route('/')
@app.route('/')
def index():
return b"Hello, World"
def wsgi(env, start):
c = b"Hello, World"
start("200 OK", [('Content-Type', 'text/plain'), ('Content-Length', str(len(c)))])
return [c]
#starting method
# Flask: gunicorn -k meinheld.gmeinheld.MeinheldWorker -b :6000 app:app
# Bottle: gunicorn -k meinheld.gmeinheld.MeinheldWorker -b :6000 app:bottle_app
# wsgi: gunicorn -k meinheld.gmeinheld.MeinheldWorker -b :6000 app:wsgi
Recommended Posts