├── .gitignore ├── README.md ├── app_aio.py ├── app_bottle.py ├── app_falcon.py ├── app_flask.py ├── app_sanic.py ├── app_starlette.py ├── async_db.py ├── gen_test_data.py ├── nginx.conf ├── requirements.txt ├── requirements_frozen.txt ├── run-benchmark.sh ├── runs ├── aiohttp.txt ├── daphne.txt ├── gunicorn-event.txt ├── gunicorn-flask.txt ├── gunicorn-meinheld-bottle.txt ├── gunicorn-meinheld-falcon.txt ├── gunicorn-meinheld-flask.txt ├── uvicorn-sanic-no-nginx.txt ├── uvicorn-sanic.txt ├── uvicorn-starlette-no-nginx.txt ├── uvicorn-starlette.txt ├── uwsgi-bottle-no-nginx.txt ├── uwsgi-bottle-own-proto.txt ├── uwsgi-bottle.txt ├── uwsgi-falcon.txt └── uwsgi-flask.txt ├── schema.sql ├── serve-aiohttp.sh ├── serve-daphne-starlette.sh ├── serve-gunicorn-flask.sh ├── serve-gunicorn-gevent-flask.sh ├── serve-gunicorn-meinheld-bottle.sh ├── serve-gunicorn-meinheld-falcon.sh ├── serve-gunicorn-meinheld-flask.sh ├── serve-sanic-own.sh ├── serve-uvicorn-sanic.sh ├── serve-uvicorn-starlette.sh ├── serve-uwsgi-bottle-own-proto.sh ├── serve-uwsgi-bottle.sh ├── serve-uwsgi-falcon.sh ├── serve-uwsgi-flask.sh └── sync_db.py /.gitignore: -------------------------------------------------------------------------------- 1 | .mypy_cache/ 2 | __pycache__/ 3 | test.csv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python webserver performance comparison 2 | 3 | This repository holds some benchmarking configuration for a number of popular 4 | python webserver configurations: 5 | 6 | - nginx and aiohttp 7 | - nginx, gunicorn and flask 8 | - nginx, uvicorn and starlette 9 | - nginx, uwsgi and flask 10 | 11 | These are benchmarked by apache-bench. 12 | 13 | The nginx configuration is included, which listens on port 8001. 14 | 15 | The servers are run from shell scripts. 16 | 17 | ## $PWPWORKERS 18 | 19 | This variable controls how many workers are started. Most of the async servers 20 | don't benefit from many more workers than cores available. Most of the sync 21 | workers do benefit from this though (often 2*`nproc`) is a good starting point. 22 | 23 | Except for Daphne - see below. 24 | 25 | ## Daphne 26 | 27 | Daphne doesn't have a front facing proxy and so requires nginx to do the load 28 | balancing. Not a bad idea but means that to test it you need to edit nginx's 29 | conf and start multiple instances. 30 | 31 | https://github.com/django/daphne/issues/79#issuecomment-278188287 32 | 33 | ## Slow ttys 34 | 35 | Some terminals are quite slow, for example gnome-terminal. Many of the wsgi 36 | servers output an access log to either stdout and stderr (uwsgi, daphne and 37 | uvicorn) - that needs to be redirected to a `/dev/null` to ensure a fair 38 | comparison. 39 | 40 | ## Test data 41 | 42 | Test data is generated by `gen_test_data.py` to produce a CSV which you then 43 | copy into a postgres database that has `schema.sql` loaded. 44 | 45 | ## Outcomes 46 | 47 | In `/runs` 48 | -------------------------------------------------------------------------------- /app_aio.py: -------------------------------------------------------------------------------- 1 | import json 2 | from aiohttp import web 3 | import aiopg 4 | 5 | from async_db import get_row 6 | 7 | async def handle(request): 8 | a, b = await get_row() 9 | return web.Response(text=json.dumps({"a": str(a).zfill(10), "b": b})) 10 | 11 | 12 | app = web.Application() 13 | app.add_routes([web.get('/test', handle)]) 14 | -------------------------------------------------------------------------------- /app_bottle.py: -------------------------------------------------------------------------------- 1 | import bottle 2 | import json 3 | 4 | from sync_db import get_row 5 | 6 | app = bottle.Bottle() 7 | 8 | @app.route("/test") 9 | def test(): 10 | a, b = get_row() 11 | return json.dumps({"a": str(a).zfill(10), "b": b}) 12 | -------------------------------------------------------------------------------- /app_falcon.py: -------------------------------------------------------------------------------- 1 | import falcon 2 | import json 3 | 4 | from sync_db import get_row 5 | 6 | 7 | class ThingResource: 8 | def on_get(self, req, resp): 9 | a, b = get_row() 10 | resp.body = json.dumps({"a": str(a).zfill(10), "b": b}) 11 | 12 | app = falcon.API() 13 | 14 | things = ThingResource() 15 | 16 | app.add_route("/test", things) 17 | -------------------------------------------------------------------------------- /app_flask.py: -------------------------------------------------------------------------------- 1 | import flask 2 | import json 3 | from sync_db import get_row 4 | 5 | app = flask.Flask("python-web-perf") 6 | 7 | pool = None 8 | 9 | @app.route("/test") 10 | def test(): 11 | a, b = get_row() 12 | return json.dumps({"a": str(a).zfill(10), "b": b}) 13 | -------------------------------------------------------------------------------- /app_sanic.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | from sanic import Sanic 4 | from sanic.response import json 5 | 6 | from async_db import get_row 7 | 8 | app = Sanic("python-web-perf") 9 | 10 | 11 | @app.route("/test") 12 | async def test(request): 13 | a, b = await get_row() 14 | return json({"a": str(a).zfill(10), "b": b}) 15 | 16 | 17 | if __name__ == "__main__": 18 | app.run(host="127.0.0.1", port=8001, workers=int(environ["PWPWORKERS"])) 19 | -------------------------------------------------------------------------------- /app_starlette.py: -------------------------------------------------------------------------------- 1 | from starlette.applications import Starlette 2 | from starlette.responses import JSONResponse 3 | from starlette.routing import Route 4 | 5 | from async_db import get_row 6 | 7 | 8 | async def homepage(request): 9 | a, b = await get_row() 10 | return JSONResponse({"a": str(a).zfill(10), "b": b}) 11 | 12 | 13 | routes = [ 14 | Route("/test", endpoint=homepage) 15 | ] 16 | 17 | 18 | app = Starlette(routes=routes) 19 | -------------------------------------------------------------------------------- /async_db.py: -------------------------------------------------------------------------------- 1 | import random 2 | import aiopg 3 | 4 | pool = None 5 | 6 | async def get_pool(): 7 | global pool 8 | if pool is None: 9 | pool = await aiopg.create_pool( 10 | "dbname=test user=test password=test port=6432 host=127.0.0.1") 11 | return pool 12 | 13 | max_n = 1000_000 - 1 14 | 15 | async def get_row(): 16 | pool = await get_pool() 17 | async with pool.acquire() as conn: 18 | async with conn.cursor() as cursor: 19 | index = random.randint(1, max_n) 20 | await cursor.execute("select a, b from test where a = %s", (index,)) 21 | ((a, b),) = await cursor.fetchall() 22 | return a, b 23 | -------------------------------------------------------------------------------- /gen_test_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import csv 3 | import sys 4 | import random 5 | import string 6 | 7 | # static seed to generate the same data all the time 8 | rng = random.Random(0) 9 | 10 | csv_writer = csv.writer(sys.stdout) 11 | csv_writer.writerow(["a", "b"]) 12 | 13 | for i in range(1_000_000): 14 | short_random_string = "".join( 15 | rng.choice(string.ascii_letters) for _ in range(20)) 16 | csv_writer.writerow([i, short_random_string]) 17 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8000; 3 | server_name _; 4 | location / { 5 | proxy_pass http://localhost:8001; 6 | # uwsgi_pass localhost:8001; 7 | # include uwsgi_params; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp[speedups]~=3.6.2 2 | flask~=1.1.0 3 | gunicorn~=20.0.0 4 | starlette~=0.13.4 5 | uvicorn~=0.11.5 6 | uwsgi~=2.0.0 7 | aiopg~=1.0.0 8 | psycopg2~=2.8.5 9 | gevent~=20.5.0 10 | meinheld~=1.0.1 11 | falcon~=2.0.0 12 | sanic~=19.12.2 13 | bottle~=0.12.18 14 | daphne~=2.5.0 15 | -------------------------------------------------------------------------------- /requirements_frozen.txt: -------------------------------------------------------------------------------- 1 | -f /home/cal/.pip/wheelhouse/ 2 | aiodns==2.0.0 3 | aiofiles==0.5.0 4 | aiohttp==3.6.2 5 | aiopg==1.0.0 6 | aiosqlite==0.13.0 7 | asgiref==3.2.7 8 | async-timeout==3.0.1 9 | attrs==19.3.0 10 | autobahn==20.4.3 11 | autoflake==1.3.1 12 | Automat==20.2.0 13 | bottle==0.12.18 14 | brotlipy==0.7.0 15 | cchardet==2.1.6 16 | certifi==2020.4.5.1 17 | cffi==1.14.0 18 | chardet==3.0.4 19 | click==7.1.2 20 | constantly==15.1.0 21 | cryptography==2.9.2 22 | daphne==2.5.0 23 | entrypoints==0.3 24 | falcon==2.0.0 25 | flake8==3.7.9 26 | Flask==1.1.2 27 | gevent==20.5.0 28 | greenlet==0.4.15 29 | gunicorn==20.0.4 30 | h11==0.9.0 31 | h2==3.2.0 32 | hpack==3.0.0 33 | hstspreload==2020.5.5 34 | httptools==0.1.1 35 | httpx==0.9.3 36 | hyperframe==5.2.0 37 | hyperlink==19.0.0 38 | idna==2.9 39 | incremental==17.5.0 40 | itsdangerous==1.1.0 41 | Jinja2==2.11.2 42 | MarkupSafe==1.1.1 43 | mccabe==0.6.1 44 | meinheld==1.0.1 45 | multidict==4.7.5 46 | psycopg2==2.8.5 47 | psycopg2-binary==2.8.5 48 | pyasn1==0.4.8 49 | pyasn1-modules==0.2.8 50 | pycares==3.1.1 51 | pycodestyle==2.5.0 52 | pycparser==2.20 53 | pyflakes==2.1.1 54 | PyHamcrest==2.0.2 55 | pyOpenSSL==19.1.0 56 | rfc3986==1.4.0 57 | sanic==19.12.2 58 | service-identity==18.1.0 59 | six==1.14.0 60 | sniffio==1.1.0 61 | starlette==0.13.4 62 | Twisted==20.3.0 63 | txaio==20.4.1 64 | ujson==2.0.3 65 | uvicorn==0.11.5 66 | uvloop==0.14.0 67 | uWSGI==2.0.18 68 | websockets==8.1 69 | Werkzeug==1.0.1 70 | yarl==1.4.2 71 | zope.interface==5.1.0 72 | -------------------------------------------------------------------------------- /run-benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONCURRENCY=100 4 | REQUEST_COUNT=100000 5 | 6 | ab -c $CONCURRENCY -n $REQUEST_COUNT http://localhost:8000/test 7 | -------------------------------------------------------------------------------- /runs/aiohttp.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 22.218 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21600000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 4500.76 [#/sec] (mean) 22 | Time per request: 22.218 [ms] (mean) 23 | Time per request: 0.222 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 949.38 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.5 0 28 29 | Processing: 1 21 14.5 18 215 30 | Waiting: 1 21 14.5 17 215 31 | Total: 1 22 14.5 19 215 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 19 35 | 66% 24 36 | 75% 27 37 | 80% 30 38 | 90% 39 39 | 95% 48 40 | 98% 63 41 | 99% 76 42 | 100% 215 (longest request) 43 | -------------------------------------------------------------------------------- /runs/daphne.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 45 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 37.347 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 20400000 bytes 20 | HTML transferred: 4500000 bytes 21 | Requests per second: 2677.60 [#/sec] (mean) 22 | Time per request: 37.347 [ms] (mean) 23 | Time per request: 0.373 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 533.43 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 0.9 0 25 29 | Processing: 1 37 70.5 19 1356 30 | Waiting: 1 36 70.5 19 1356 31 | Total: 1 37 70.5 20 1357 32 | WARNING: The median and mean for the initial connection time are not within a normal deviation 33 | These results are probably not that reliable. 34 | 35 | Percentage of the requests served within a certain time (ms) 36 | 50% 20 37 | 66% 30 38 | 75% 38 39 | 80% 44 40 | 90% 71 41 | 95% 123 42 | 98% 252 43 | 99% 364 44 | 100% 1357 (longest request) 45 | -------------------------------------------------------------------------------- /runs/gunicorn-event.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 32.495 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 3077.35 [#/sec] (mean) 22 | Time per request: 32.495 [ms] (mean) 23 | Time per request: 0.325 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 646.12 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 0 0.8 0 20 29 | Processing: 1 32 28.5 23 289 30 | Waiting: 1 32 28.5 23 289 31 | Total: 1 32 28.5 24 290 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 24 35 | 66% 34 36 | 75% 43 37 | 80% 49 38 | 90% 69 39 | 95% 89 40 | 98% 115 41 | 99% 136 42 | 100% 290 (longest request) 43 | -------------------------------------------------------------------------------- /runs/gunicorn-flask.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 28.792 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 3473.18 [#/sec] (mean) 22 | Time per request: 28.792 [ms] (mean) 23 | Time per request: 0.288 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 729.23 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 0 0.6 0 14 29 | Processing: 3 28 3.9 28 63 30 | Waiting: 3 28 3.9 28 63 31 | Total: 6 29 3.8 28 63 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 28 35 | 66% 29 36 | 75% 30 37 | 80% 31 38 | 90% 33 39 | 95% 36 40 | 98% 40 41 | 99% 42 42 | 100% 63 (longest request) 43 | -------------------------------------------------------------------------------- /runs/gunicorn-meinheld-bottle.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 17.302 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 5779.57 [#/sec] (mean) 22 | Time per request: 17.302 [ms] (mean) 23 | Time per request: 0.173 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 1213.48 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.3 0 13 29 | Processing: 1 16 4.5 16 85 30 | Waiting: 1 16 4.5 16 85 31 | Total: 2 17 4.2 17 91 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 17 35 | 66% 18 36 | 75% 19 37 | 80% 20 38 | 90% 22 39 | 95% 24 40 | 98% 29 41 | 99% 32 42 | 100% 91 (longest request) 43 | -------------------------------------------------------------------------------- /runs/gunicorn-meinheld-falcon.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 17.892 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 20700000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 5589.11 [#/sec] (mean) 22 | Time per request: 17.892 [ms] (mean) 23 | Time per request: 0.179 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 1129.83 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.4 0 11 29 | Processing: 2 17 4.5 17 97 30 | Waiting: 2 17 4.5 17 97 31 | Total: 5 18 4.2 17 97 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 17 35 | 66% 19 36 | 75% 20 37 | 80% 21 38 | 90% 23 39 | 95% 25 40 | 98% 28 41 | 99% 31 42 | 100% 97 (longest request) 43 | -------------------------------------------------------------------------------- /runs/gunicorn-meinheld-flask.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 22.168 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 4510.95 [#/sec] (mean) 22 | Time per request: 22.168 [ms] (mean) 23 | Time per request: 0.222 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 947.12 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.0 0 17 29 | Processing: 2 22 4.3 21 69 30 | Waiting: 1 21 4.2 21 69 31 | Total: 5 22 4.0 21 72 32 | WARNING: The median and mean for the initial connection time are not within a normal deviation 33 | These results are probably not that reliable. 34 | 35 | Percentage of the requests served within a certain time (ms) 36 | 50% 21 37 | 66% 23 38 | 75% 24 39 | 80% 25 40 | 90% 27 41 | 95% 29 42 | 98% 32 43 | 99% 35 44 | 100% 72 (longest request) 45 | -------------------------------------------------------------------------------- /runs/uvicorn-sanic-no-nginx.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | -------------------------------------------------------------------------------- /runs/uvicorn-sanic.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 45 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 21.334 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 20400000 bytes 20 | HTML transferred: 4500000 bytes 21 | Requests per second: 4687.29 [#/sec] (mean) 22 | Time per request: 21.334 [ms] (mean) 23 | Time per request: 0.213 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 933.80 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.6 0 16 29 | Processing: 1 20 16.1 16 250 30 | Waiting: 1 20 16.1 15 250 31 | Total: 1 21 16.2 17 251 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 17 35 | 66% 22 36 | 75% 26 37 | 80% 29 38 | 90% 40 39 | 95% 51 40 | 98% 68 41 | 99% 85 42 | 100% 251 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uvicorn-starlette-no-nginx.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: uvicorn 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8001 11 | 12 | Document Path: /test 13 | Document Length: 45 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 17.078 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 17000000 bytes 20 | HTML transferred: 4500000 bytes 21 | Requests per second: 5855.45 [#/sec] (mean) 22 | Time per request: 17.078 [ms] (mean) 23 | Time per request: 0.171 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 972.10 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 2 2.0 1 15 29 | Processing: 1 15 11.3 12 159 30 | Waiting: 1 14 10.9 11 156 31 | Total: 1 17 11.5 14 162 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 14 35 | 66% 18 36 | 75% 21 37 | 80% 23 38 | 90% 30 39 | 95% 37 40 | 98% 49 41 | 99% 60 42 | 100% 162 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uvicorn-starlette.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 45 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 20.196 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 20400000 bytes 20 | HTML transferred: 4500000 bytes 21 | Requests per second: 4951.56 [#/sec] (mean) 22 | Time per request: 20.196 [ms] (mean) 23 | Time per request: 0.202 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 986.44 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.6 0 16 29 | Processing: 1 19 14.7 15 196 30 | Waiting: 1 18 14.6 15 196 31 | Total: 1 20 14.8 16 197 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 16 35 | 66% 21 36 | 75% 25 37 | 80% 28 38 | 90% 37 39 | 95% 49 40 | 98% 64 41 | 99% 75 42 | 100% 197 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uwsgi-bottle-no-nginx.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8001 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 16.045 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 12700000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 6232.63 [#/sec] (mean) 22 | Time per request: 16.045 [ms] (mean) 23 | Time per request: 0.160 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 772.99 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.2 0 12 29 | Processing: 3 15 3.7 15 53 30 | Waiting: 2 15 3.6 14 53 31 | Total: 7 16 3.3 15 61 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 15 35 | 66% 17 36 | 75% 18 37 | 80% 19 38 | 90% 21 39 | 95% 23 40 | 98% 24 41 | 99% 26 42 | 100% 61 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uwsgi-bottle-own-proto.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 18.933 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 5281.69 [#/sec] (mean) 22 | Time per request: 18.933 [ms] (mean) 23 | Time per request: 0.189 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 1108.95 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.3 0 18 29 | Processing: 2 18 5.1 18 69 30 | Waiting: 2 18 5.0 18 68 31 | Total: 4 19 4.9 18 69 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 18 35 | 66% 20 36 | 75% 21 37 | 80% 22 38 | 90% 24 39 | 95% 27 40 | 98% 31 41 | 99% 36 42 | 100% 69 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uwsgi-bottle.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 18.191 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 5497.31 [#/sec] (mean) 22 | Time per request: 18.191 [ms] (mean) 23 | Time per request: 0.182 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 1154.22 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.2 0 19 29 | Processing: 2 17 4.3 17 77 30 | Waiting: 2 17 4.2 17 77 31 | Total: 4 18 4.0 18 77 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 18 35 | 66% 19 36 | 75% 20 37 | 80% 21 38 | 90% 22 39 | 95% 24 40 | 98% 28 41 | 99% 32 42 | 100% 77 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uwsgi-falcon.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 18.465 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 20700000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 5415.62 [#/sec] (mean) 22 | Time per request: 18.465 [ms] (mean) 23 | Time per request: 0.185 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 1094.76 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.3 0 20 29 | Processing: 2 18 5.5 17 84 30 | Waiting: 2 17 5.4 17 83 31 | Total: 3 18 5.4 18 84 32 | 33 | Percentage of the requests served within a certain time (ms) 34 | 50% 18 35 | 66% 19 36 | 75% 21 37 | 80% 22 38 | 90% 25 39 | 95% 28 40 | 98% 33 41 | 99% 37 42 | 100% 84 (longest request) 43 | -------------------------------------------------------------------------------- /runs/uwsgi-flask.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1843412 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking 95.217.4.157 (be patient) 6 | 7 | 8 | Server Software: nginx/1.17.10 9 | Server Hostname: 95.217.4.157 10 | Server Port: 8000 11 | 12 | Document Path: /test 13 | Document Length: 48 bytes 14 | 15 | Concurrency Level: 100 16 | Time taken for tests: 22.570 seconds 17 | Complete requests: 100000 18 | Failed requests: 0 19 | Total transferred: 21500000 bytes 20 | HTML transferred: 4800000 bytes 21 | Requests per second: 4430.66 [#/sec] (mean) 22 | Time per request: 22.570 [ms] (mean) 23 | Time per request: 0.226 [ms] (mean, across all concurrent requests) 24 | Transfer rate: 930.27 [Kbytes/sec] received 25 | 26 | Connection Times (ms) 27 | min mean[+/-sd] median max 28 | Connect: 0 1 1.0 0 19 29 | Processing: 3 22 3.8 22 69 30 | Waiting: 3 22 3.8 22 69 31 | Total: 9 23 3.5 22 74 32 | WARNING: The median and mean for the initial connection time are not within a normal deviation 33 | These results are probably not that reliable. 34 | 35 | Percentage of the requests served within a certain time (ms) 36 | 50% 22 37 | 66% 23 38 | 75% 24 39 | 80% 25 40 | 90% 27 41 | 95% 28 42 | 98% 31 43 | 99% 33 44 | 100% 74 (longest request) 45 | -------------------------------------------------------------------------------- /schema.sql: -------------------------------------------------------------------------------- 1 | create table test (a int primary key, b text); 2 | -------------------------------------------------------------------------------- /serve-aiohttp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn app_aio:app -w $PWPWORKERS --bind localhost:8001 --worker-class aiohttp.GunicornWebWorker 4 | -------------------------------------------------------------------------------- /serve-daphne-starlette.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | daphne -b 0.0.0.0 -p 8001 app_starlette:app 4 | -------------------------------------------------------------------------------- /serve-gunicorn-flask.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn -w $PWPWORKERS --bind :8001 app_flask:app 4 | -------------------------------------------------------------------------------- /serve-gunicorn-gevent-flask.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn --bind :8001 -w $PWPWORKERS app_flask:app --worker-class gunicorn.workers.ggevent.GeventWorker 4 | -------------------------------------------------------------------------------- /serve-gunicorn-meinheld-bottle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn --bind :8001 -w $PWPWORKERS app_bottle:app --worker-class "egg:meinheld#gunicorn_worker" 4 | -------------------------------------------------------------------------------- /serve-gunicorn-meinheld-falcon.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn --bind :8001 -w $PWPWORKERS app_falcon:app --worker-class "egg:meinheld#gunicorn_worker" 4 | -------------------------------------------------------------------------------- /serve-gunicorn-meinheld-flask.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | gunicorn --bind :8001 -w $PWPWORKERS app_flask:app --worker-class "egg:meinheld#gunicorn_worker" 4 | -------------------------------------------------------------------------------- /serve-sanic-own.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | python3 app_sanic.py 4 | -------------------------------------------------------------------------------- /serve-uvicorn-sanic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uvicorn --port 8001 --workers $PWPWORKERS app_sanic:app 4 | -------------------------------------------------------------------------------- /serve-uvicorn-starlette.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uvicorn --port 8001 --workers $PWPWORKERS app_starlette:app 4 | -------------------------------------------------------------------------------- /serve-uwsgi-bottle-own-proto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uwsgi --uwsgi-socket :8001 -w app_bottle:app --processes $PWPWORKERS 4 | -------------------------------------------------------------------------------- /serve-uwsgi-bottle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uwsgi --http-socket :8001 -w app_bottle:app --processes $PWPWORKERS 4 | -------------------------------------------------------------------------------- /serve-uwsgi-falcon.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uwsgi --http-socket :8001 -w app_falcon:app --processes $PWPWORKERS 4 | -------------------------------------------------------------------------------- /serve-uwsgi-flask.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uwsgi --http-socket :8001 -w app_flask:app --processes $PWPWORKERS 4 | -------------------------------------------------------------------------------- /sync_db.py: -------------------------------------------------------------------------------- 1 | import psycopg2 2 | import psycopg2.pool 3 | import random 4 | 5 | pool = None 6 | 7 | def get_pool(): 8 | global pool 9 | if pool is None: 10 | pool = psycopg2.pool.SimpleConnectionPool( 11 | 1, 4, database="test", user="test", password="test", port=6432, 12 | ) 13 | return pool 14 | 15 | 16 | max_n = 1000_000 - 1 17 | 18 | 19 | def get_row(): 20 | conn = get_pool().getconn() 21 | cursor = conn.cursor() 22 | index = random.randint(1, max_n) 23 | cursor.execute("select a, b from test where a = %s;", (index,)) 24 | ((a, b),) = cursor.fetchall() 25 | cursor.close() 26 | get_pool().putconn(conn) 27 | return a, b 28 | --------------------------------------------------------------------------------