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アプリから
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を調べてみる
#!/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アプリ
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で接続するアプリを作る。
#!/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の外側で接続と切断をやる。標準出力への表示は遅くなる元なのでそれをやめた。
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)
だいぶ高速化した。