32
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

go言語のwebフレームワークechoとpythonのwebフレームワークflaskの速度を比較してみた

Last updated at Posted at 2016-08-11

golang-echoとpython-flaskの実行速度を比較してみる

今までずっとpythonを触っていて特に不満もなかったが、最近golangを触っている。
何かをちょこちょこと作るのにpythonは便利だが速度が必要なときにgolangを使ってどの程度早くなるのか感覚的に掴みたいと思い、golangで代表的なwebフレームワークechoとpythonの軽量webframeworkのflaskを比較して、どの程度速度に差があるか調べてみた。

テスト環境

vmware上に構築したubuntu 16.04 hddは普通のやつでssdではない。
i7,memory 6GB
python2.7
golang 1.6

シンプルな文字列を返すwebアプリ

まず感触をつかむために簡単なwebアプリを作ってみる

golang-echoを使ったシンプルなアプリ

まずは、golangを使ったシンプルなwebアプリから

simple_server.go

package main
import (
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"
)
func main() {
        e := echo.New()
        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })
        e.Run(standard.New(":1323"))
}

golangでechoライブラリを使えばこれだけ書けば、Hello World を返すwebアプリになり手軽だ。

golang-echoの速度の速度を計測してみる

apach benchを使って速度を見てみる

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 100000 -c 100 http://localhost:1323/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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 10000 requests
...
Completed 100000 requests
Finished 100000 requests

Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   9.525 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      13000000 bytes
HTML transferred:       1300000 bytes
Requests per second:    10498.93 [#/sec] (mean)
Time per request:       9.525 [ms] (mean)
Time per request:       0.095 [ms] (mean, across all concurrent requests)
Transfer rate:          1332.87 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   2.8      2      19
Processing:     0    6   3.2      7      31
Waiting:        0    5   2.5      4      26
Total:          0    9   4.1      9      33

Percentage of the requests served within a certain time (ms)
  50%      9
  66%     11
  75%     12
  80%     13
  90%     15
  95%     17
  98%     18
  99%     19
100% 33 (longest request)

Requests per second: 10498.93 [#/sec] (mean)

と対してチューニングしなくても1万req/secくらい出るようだ

python-flaskを使ったシンプルなアプリ

次にflaskを調べてみる

simple_server.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == '__main__':
app.run()

flaskもこれで、Hello World を返す

python-flaskのベンチマーク

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 10000 -c 100  http://localhost:5000/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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 10000 requests
Finished 10000 requests


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

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      100
Time taken for tests:   8.190 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1680000 bytes
HTML transferred:       120000 bytes
Requests per second:    1220.97 [#/sec] (mean)
Time per request:       81.902 [ms] (mean)
Time per request:       0.819 [ms] (mean, across all concurrent requests)
Transfer rate:          200.32 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.7      0       8
Processing:     2   81  12.6     81     148
Waiting:        1   81  12.5     81     148
Total:          6   81  12.2     81     148

Percentage of the requests served within a certain time (ms)
  50%     81
  66%     90
  75%     91
  80%     92
  90%     95
  95%     98
  98%    101
  99%    106
 100%    148 (longest request)

Requests per second: 1220.97 [#/sec] (mean)

特に何もせず、1220req/sec位出る

Mongoに接続して、検索して結果を返すwebアプリ

シンプルなアプリだと現実の問題とずれてしまうので、mongoに接続し、検索して結果を返すなど、少し現実よりのものを入れる。
こちらのサイトを参考にした。

golang-echoを使ったmongoに接続するwebアプリ

mgo_server.go

package main

import (
        // サーバ系
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"

        // mongo系
        "fmt"
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"

        // 型
        "strconv"
        //"reflect"
)

// ユーザクラス
type User struct {
        Name  string `bson:"name"`
        Id int `bson:"id"`
}

func main() {
        e := echo.New()
        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })

        // :id, :name に値が入っていないとNot Foundになる
        e.GET("/users/id/:id", func(c echo.Context) error {

                // 数値に変換する必要あり
                var id int
                id, _ = strconv.Atoi(c.Param("id"))

                //idでDBを引く
                session, _ := mgo.Dial("mongodb://localhost")
                defer session.Close()
                db := session.DB("test")

                // id指定でユーザ取得
                var results []User
                fmt.Println(id)
                db.C("user").Find(bson.M{"id": id}).All(&results)

                fmt.Println("Results of one user: ", results)
                fmt.Println(len(results))

                if len(results) == 0 {
                        return c.String(http.StatusOK, "No user")
                }else{
                        name := results[0].Name
                        return c.String(http.StatusOK, "Hello, " + name)
                }
        })

        e.POST("/user", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, POST user")
        })

        // ポート
        e.Run(standard.New(":1323"))
}

golang-echo mongo接続のベンチマーク

mongoに接続して、結果を返すもののベンチマーク

shibacow@ubuntu:~/prog/golang/echo_test$ ab -n 10000 -c 100  http://localhost:1323/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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 10000 requests
Finished 10000 requests

Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /users/id/1
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   9.156 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1280000 bytes
HTML transferred:       110000 bytes
Requests per second:    1092.21 [#/sec] (mean)
Time per request:       91.557 [ms] (mean)
Time per request:       0.916 [ms] (mean, across all concurrent requests)
Transfer rate:          136.53 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   4.4      0      29
Processing:     9   90  24.9     88     213
Waiting:        9   89  25.1     87     213
Total:         25   91  24.3     89     213

Percentage of the requests served within a certain time (ms)
  50%     89
  66%    100
  75%    107
  80%    112
  90%    123
  95%    134
  98%    147
  99%    156
 100%    213 (longest request)

Requests per second: 1092.21 [#/sec] (mean)

pytho-flask を使ってmongoを接続し、結果を取得する

mongoで接続するアプリを作る。

mongo_server.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from flask import Flask
from flask_pymongo import PyMongo
app = Flask(__name__)
app.config['MONGO_HOST']='localhost'
app.config['MONGO_DBNAME'] = 'test'
mongo = PyMongo(app)

@app.route("/users/id/<int:id>")
def user_id(id):
    user= mongo.db.user.find_one({"id":id})
    msg="Hello id={} name={}".format(user["id"],user['name'])
    return msg

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == '__main__':
    app.run(host="0.0.0.0",debug=False)

python-flaskを使ったベンチマーク

shibacow@ubuntu:~/prog/python/flask_test$ ab -n 10000 -c100 http://localhost:500
0/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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 10000 requests
Finished 10000 requests

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

Document Path:          /users/id/1
Document Length:        20 bytes

Concurrency Level:      100
Time taken for tests:   12.639 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1760000 bytes
HTML transferred:       200000 bytes
Requests per second:    791.22 [#/sec] (mean)
Time per request:       126.387 [ms] (mean)
Time per request:       1.264 [ms] (mean, across all concurrent requests)
Transfer rate:          135.99 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.7      0       8
Processing:     6  126  11.8    125     164
Waiting:        6  125  11.8    125     163
Total:         11  126  11.5    125     164

Percentage of the requests served within a certain time (ms)
  50%    125
  66%    129
  75%    131
  80%    132
  90%    138
  95%    143
  98%    149
  99%    153
 100%    164 (longest request)

Requests per second: 791.22 [#/sec] (mean)

まとめ

golang-echoとpython-flaskの速度を比較した。

比較 シンプルなアプリ 複雑なアプリ
golang-echo 10498 req/sec 1092 req/sec
python-flask 1220 req/sec 791 req/sec

シンプルなアプリでは、10倍位性能が違うが、mongoに接続する場合は、そこまで速度差が出ない。リクエストのたびにmongoに接続するのでそちらの方が、負荷が高いのかもしれない。echoもpymongoもコネクションプールを使えばまた結果は変わるのかもしれない。
golangとpythonを比較したのは別にpythonの性能を貶めたいわけではなく、単に大体どこくらい差があるのかを体感したいからだ。

まとめ(追記)

下記追記にあるが、mongoへの毎回への接続をやめて、一回接続をして使い回し用にしたら、だいぶ高速化した。ただ、セッションが状態を持ち続けるので、例えばアップデートしたりとか、コミットしたりするときにどのようになるかはわからないので、ベンチマーク以外の本番環境で使うには検証が必要だろう。

比較 シンプルなアプリ 複雑なアプリ
golang-echo 10498 req/sec 1092 req/sec
golang-echo(下記改良版) なし 6283.74 req/sec
python-flask 1220 req/sec 791 req/sec

追記 golang-echo mongo接続して結果を取得の高速化

golang-echo mongo接続改良

上の例では、一リクエストあたり、mongoへの接続と切断を繰り返していた。それをやめてGETの外側で接続と切断をやる。標準出力への表示は遅くなる元なのでそれをやめた。

mgo_server.go
package main

import (
        // サーバ系
        "net/http"
        "github.com/labstack/echo"
        "github.com/labstack/echo/engine/standard"

        // mongo系
        //"fmt" //標準出力は遅いので、止める
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"

        // 型
        "strconv"
        //"reflect"
)

// ユーザクラス
type User struct {
        Name  string `bson:"name"`
        Id int `bson:"id"`
}

func main() {
        e := echo.New()
        //idでDBを引くGETの外側に出して、毎回の接続・切断をやめた
        session, _ := mgo.Dial("mongodb://localhost")
        defer session.Close()
        db := session.DB("test")

        e.GET("/", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, World!")
        })

        // :id, :name に値が入っていないとNot Foundになる
        e.GET("/users/id/:id", func(c echo.Context) error {

                // 数値に変換する必要あり
                var id int
                id, _ = strconv.Atoi(c.Param("id"))


                // id指定でユーザ取得
                var results []User
                //fmt.Println(id)
                db.C("user").Find(bson.M{"id": id}).All(&results)

                //fmt.Println("Results of one user: ", results)
                //fmt.Println(len(results))

                if len(results) == 0 {
                        return c.String(http.StatusOK, "No user")
                }else{
                        name := results[0].Name
                        return c.String(http.StatusOK, "Hello, " + name)
                }
        })

        e.POST("/user", func(c echo.Context) error {
                return c.String(http.StatusOK, "Hello, POST user")
        })

        // ポート
        e.Run(standard.New(":1323"))
}

改良版ベンチマーク

上記改良を行ったところ、だいぶ高速化し、6282 req/secになった。

shibacow@ubuntu:~$ ab -n 100000 -c 100 http://localhost:1323/users/id/1
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
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 10000 requests
....
Completed 100000 requests
Finished 100000 requests


Server Software:
Server Hostname:        localhost
Server Port:            1323

Document Path:          /users/id/1
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   15.914 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      12800000 bytes
HTML transferred:       1100000 bytes
Requests per second:    6283.74 [#/sec] (mean)
Time per request:       15.914 [ms] (mean)
Time per request:       0.159 [ms] (mean, across all concurrent requests)
Transfer rate:          785.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4   3.2      3      21
Processing:     0   12   5.0     12      44
Waiting:        0   10   4.9     10      44
Total:          0   16   5.2     15      45

Percentage of the requests served within a certain time (ms)
  50%     15
  66%     17
  75%     19
  80%     20
  90%     22
  95%     25
  98%     29
  99%     32
 100%     45 (longest request)

Requests per second: 6283.74 [#/sec] (mean)

だいぶ高速化した。

32
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?