Help us understand the problem. What is going on with this article?

wsgi, Bottle, Flask の速度差

More than 5 years have passed since last update.

ISUCON の季節ですね。 ISUCON では慣習的に各言語で代表的なマイクロフレームワークが使われるのですが、 Python では今のところ Flask がずっと使われています。

Flask は確かに、簡単なサンプルアプリを書くときの見た目はマイクロフレームワークになっています。
しかし、構造的には沢山のフック、シグナルがあったりしていて、重量級の設計になっています。

Flask 本体と Werkzeug を合わせると数万行のサイズです。単なる Hello World アプリでも、数十の関数呼び出しが裏で動いています。

Bottle も、 Flask と同じくマルチスレッド対応で、スレッドローカルを使ったコンテキストスタックがある、拡張機能もあるフレームワークですが、構造は Flask よりも大分質素です。
ソースコードも1ファイル3000行代で、その分フレームワークのオーバーヘッドも Flask の半分程度になっています。

Hello アプリでちょっとした計測をしてみました。 MacBook Air 2013 Mid (Core i5 1.3GHz) で、シングルスレッド・シングルプロセスの Hello アプリを、 wrk -t1 -c1 でベンチマークします。 Python は CPython 3.4.1, Web サーバーは Meinheld (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

Avg Latency の差で見た場合、 Bottle は約 100μs, Flask は約 350μs が生 wsgi に対するオーバーヘッドになっており、トータルの性能としても倍以上の差になっています。

Bottle は Jinja テンプレートもサポートしているので、初期アプリが Flask だったとしてもそこそこ簡単に Bottle に切り替えられるはずです。
HTML の部分をキャッシュして、 Memcached から取得したデータをくっつけて返すだけのパスが沢山叩かれるようなケースでは、このベース部分のパフォーマンスが無視できない差になってくるはずです。

最後に計測にしたソースコードを載せておきます。お試しください。

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]

# 起動方法
# 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
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした