Flask+PyPy 大規模Web向けBlueprintで速度ベンチマーク取ってみた

[前回の記事](http://qiita.com/haminiku/items/4298d62cc50768c9f473)でDjangoとFlaskとPyramidを比較して、Flaskいいなーとなったので早速試してみました。

FlaskというPython Webフレームワークにて大規模向けと言われている[Blueprintチュートリアル](https://exploreflask.com/blueprints.html)に沿って構築した機能をベンチマークして比較しています。Blueprintとは複数の小さなアプリ毎（機能毎）にViewを切り分けるFlaskの機能です。

構築後Apache Benchを利用して、PyPy3 2.4とPython3.5を比較した **ベンチマーク** を取得しています。

# ディレクトリ構成
デフォルトの単一Viewと異なりviewsディレクトリを設置して、その中に機能毎のviewを設置していきます。今回は`root` と`report` 機能を追加してみました。

■ ディレクトリ構成
![スクリーンショット 2015-12-11 16.36.56.png](https://qiita-image-store.s3.amazonaws.com/0/65312/0464cd3c-c9cd-fb26-bef3-267f5809a996.png)

# テンプレートの抽象化
HTMLのHEADやBODYタグといった共通部分を`master.html` 切り分けて分離します。

```html:master.html
<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset="utf-8" />
  <title>{% block title %}{% endblock %} - Hello Flask!</title>
  <link type="text/css" rel="stylesheet"
       href="{{ url_for('static',
                        filename='hello.css')}}" />
</head>
<body>

{% block body %}{% endblock %}

</body>
</html>
```

```html:index.html
{% extends "master.html" %}

{% block title %}index{% endblock %}

{% block body %}
<h1>this is index page</h1><br />

<h3>report</h3>
<a href="{{ url_for('report.index') }}">report - index</a><br />
<a href="{{ url_for('report.report_list') }}">report - list</a><br />
{% endblock %}
```

■ index.htmlの描写結果
![スクリーンショット 2015-12-11 16.41.33.png](https://qiita-image-store.s3.amazonaws.com/0/65312/c3dca019-d88b-c566-d5d7-f7622d3be755.png)

# URLの抽象化とトップページの定義
Flaskサーバは`python main.py` コマンドで起動します。main.py に機能毎のURLを紐付けます。

```py3:main.py
# -*- coding: utf-8 -*-
from flask import Flask
from views import root, report


app = Flask(__name__)
# 機能毎のURLを定義
app.register_blueprint(root.app, url_prefix="/")
app.register_blueprint(report.app, url_prefix="/report")


if __name__ == '__main__':
    app.run(debug=True)
```

### root機能の定義
サイトトップページを定義します。

```py3:root.py
# -*- coding: utf-8 -*-
from flask import render_template, Blueprint

# 第一引数の名称が、テンプレのurl_for内で呼び出すときの名称と紐づく
app = Blueprint("index",
                __name__,
                url_prefix='/<user_url_slug>')

# テンプレート内で呼び出すときは url_for('index.index')
@app.route("/")
def index():
    return render_template('root/index.html')

```

### report機能の定義
```py3:report.py
# -*- coding: utf-8 -*-
from flask import Module, render_template, Blueprint

app = Blueprint('report',
                __name__,
                url_prefix='/<user_url_slug>')


# テンプレート内で呼び出すときは url_for('report.index')
@app.route('/', methods=['GET'], strict_slashes=False)
def index():
    return 'report index'


# テンプレート内で呼び出すときは url_for('report.report_list')
@app.route('/report_list', methods=['GET'], strict_slashes=False)
def report_list():
    return 'report - list'

```

# PyPy3 2.4とPython3.5でベンチマーク
debug=Falseと設定して、Apache Benchコマンドで並列度100で10,000回requestしてみました。Python3.5の方が26%高速に動作しました。サーバ起動は`python main.py` コマンドで行ったのでシングルプロセスでのベンチマークです。予想ではPyPyが圧倒的速度でぶっちぎると思っていたのですが蓋を開けてみたらPython3.5の方が早いという不思議な結果に終わりました。flaskは`Flask==0.10.1`です。次はDBアクセス有りで調査してみたいと思います。

![スクリーンショット 2015-12-11 17.20.24.png](https://qiita-image-store.s3.amazonaws.com/0/65312/1cfdc0c2-96e3-e0da-b2cb-4662d13cb895.png)





```shell-session:PyPy3ベンチ結果
>>>ab -n 10000 -c 100 http://localhost:5000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Werkzeug/0.11.2
Server Hostname:        localhost
Server Port:            5000

Document Path:          /
Document Length:        398 bytes

Concurrency Level:      100
Time taken for tests:   20.783 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      5530000 bytes
HTML transferred:       3980000 bytes
Requests per second:    481.16 [#/sec] (mean)
Time per request:       207.829 [ms] (mean)
Time per request:       2.078 [ms] (mean, across all concurrent requests)
Transfer rate:          259.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   2.6      1      74
Processing:     3  205  53.6    199     762
Waiting:        3  205  53.1    199     760
Total:         24  206  53.0    199     763

Percentage of the requests served within a certain time (ms)
  50%    199
  66%    204
  75%    211
  80%    214
  90%    225
  95%    236
  98%    295
  99%    571
 100%    763 (longest request)

++++++++++++++++++++++++++
>>> python --version
Python 3.2.5 (b2091e973da6, Oct 19 2014, 18:30:58)
[PyPy 2.4.0 with GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.51)]
```

```shell-session:Python3.5ベンチ結果
>>>ab -n 10000 -c 100 http://localhost:5000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Werkzeug/0.11.2
Server Hostname:        localhost
Server Port:            5000

Document Path:          /
Document Length:        398 bytes

Concurrency Level:      100
Time taken for tests:   16.380 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      5530000 bytes
HTML transferred:       3980000 bytes
Requests per second:    610.52 [#/sec] (mean)
Time per request:       163.795 [ms] (mean)
Time per request:       1.638 [ms] (mean, across all concurrent requests)
Transfer rate:          329.70 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   5.8      0     195
Processing:     3  161  44.7    148     475
Waiting:        3  161  44.5    148     475
Total:         59  162  43.9    148     476

Percentage of the requests served within a certain time (ms)
  50%    148
  66%    151
  75%    154
  80%    156
  90%    239
  95%    276
  98%    299
  99%    322
 100%    476 (longest request)

++++++++++++++++++++++++++4
>>> python --version
Python 3.5.0
```


並列度1なら平均応答速度1.8 - 2.5msで応答しました。いまのところFlaskすごく早い( ･ㅂ･)و