fapws3マジ速い?件とweb.pyのプロファイルの話
fapws3がなにやらいい感じとのPostが目立つ今日この頃。 「最速」との言葉に弱い私もweb.pyに組み込んで試してみました。 web.pyのアプリの例は 以前Postしたもの を使います。
william-os4y's fapws3 at master - GitHub
web.py + fapws3
fapws3はWSGI対応なのでweb.pyにも簡単に組み込めます。 コード全体は Sandbox 上にありますので適当に見てください。 fapws3を固有部分のみ以下に示します。
import fapws._evwsgi as evwsgi
from fapws import base
if __name__ == '__main__':
application = web.application(urls, globals()).wsgifunc()
evwsgi.start("0.0.0.0", 8080)
evwsgi.set_base_module(base)
evwsgi.wsgi_cb(("", application))
evwsgi.run()
web.py内、applicationクラスのwsgifunc()メソッドをコールしたものを fapws3のwsgi_cb()でコールバック登録します。 このあたりの書き方はfapws3サンプルにあったhello_world.py以下、 起動系のサンプルスクリプトがすべて同じ書き方になっているので それをそのまま使いました。 start()→set_base_module()→wsgi_cb()→run()のイメージですね。
ちなみにミドルウェアをかましたい場合は、以下のような感じです。 (リクエスト毎に"Hello Debug!"と出力するHelloMiddlewareミドルウェアを 追加しています。)
class HelloMiddleware(object):
def __init__(self, application):
self.application = application
def __call__(self, env, start_response):
web.debug("Hello Debug!")
return self.application(env, start_response)
if __name__ == '__main__':
application = web.application(urls, globals()).wsgifunc()
application = HelloMiddleware(application)
evwsgi.start("0.0.0.0", 8080)
evwsgi.set_base_module(base)
evwsgi.wsgi_cb(("", application))
evwsgi.run()
fapwsのコア部分はCで記述されているので、ちょっとコード読みがめんどくさいです。 興味があれば読んでみてはどうでしょうか。(私はあきらめてしまいました。)
さて性能は?
ローカルから以下コマンドで測定してみました。 5回ほど実行したおおよその平均値です。fapws速い。
$ ab -n100 http://0.0.0.0:8080/ # "Hello web.py"
$ ab -n100 http://0.0.0.0:8080/a/ # DBアクセス+テンプレート
[web.py] db,template
Requests per second: 30.40 [#/sec] (mean)
[web.py] non db,template
Requests per second: 106.45 [#/sec] (mean)
[web.py + fapws3] db,template
Requests per second: 50.49 [#/sec] (mean)
[web.py + fapws3] non db,template
Requests per second: 176.88 [#/sec] (mean)
[django mysite]
Requests per second: 14.32 [#/sec] (mean)
自分のDjangoサイトも測定してみましたが、、、思いのほかひどい結果。 それほどアクセスが無いにせよもう少し何とかしたいな。 nginx/fapws使うとか、キャッシュを有効にするためにツールで 定期的にアクセスするとか。
web.pyアプリをプロファイリング
web.py遅い遅いと言われますが、どこが遅いのか おせっかいにもプロファイルして見てみようということで、 web.pyアプリにプロファイラを仕込んでみました。
cProfile and hotshot
cProfileの結果 を見ていただければわかるのですが、 accept()待ちしているところも実行時間として測定されるので、若干期待していた 値と異なる場合があります。一回の測定時間を短くしたり、accept()待ち箇所は 測定者側で無視する等の対策が必要です。 (もう少し良い方法がないか探してみます。) hotshotでの実行結果 の結果は 小数点以下3桁くらいの表示ではあまりプロファイルとして有効なデータにはなっていませんね。
line_profiler
line_profiler は行毎の実行時間を出力してくれるPythonモジュールです。 作者の方曰くhotshotの行毎の測定は時間が掛かりすぎる云々。 確かにhotshotは測定にも解析にも時間が掛かります。 line_profilerは軽い動作でプロファイルしてくれます。 まずはインストール。
$ sudo easy_install line_profiler
使い方は測定したいメソッドを@profileでデコレートしてkernprofを「-l」指定で 実行するだけです。 実行結果は以下のようになります。
$ kernprof.py -l code.py
$ ls
code.py code.py.lprof
$ python -m line_profiler code.py.lprof
Timer unit: 1e-06 s
File: code.py
Function: GET at line 19
Total time: 0.233386 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
19 @profile
20 def GET(self, username):
21 1 4376 4376.0 1.9 users = db.select('hellouser', wh
22 1 10 10.0 0.0 isFound = False
23 2 100 50.0 0.0 for u in users:
24 1 20 20.0 0.0 if u.name == username:
25 1 6 6.0 0.0 isFound = True
26 1 7 7.0 0.0 if not isFound:
27 db.query("INSERT INTO hellous
28 1 228867 228867.0 98.1 return render.hello(username, isF
File: code.py
Function: GET at line 31
Total time: 9e-06 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
31 @profile
32 def GET(self):
33 1 9 9.0 100.0 return "Hello web.py"
内部のライブラリのコードまではプロファイルしないようです。 今回の私の用途には合いませんでしたが、特定のミドルウェアだけ 測定する、等の使い方ができるのでいいのではないでしょうか。
web.pyが遅い理由は次回書いてみます。では。
Referenced
fapws3が日本で一気に注目を集めた記事?
全文検索エンジンLuxとPythonの軽量Webアプリケーションフレームワークfapws3で構築する高速検索サービス - Future Insight
wsgiの入門記事