diff --git a/benchmarks/app/asgi.py b/benchmarks/app/asgi.py index 5297c41e..3ae7ea0b 100644 --- a/benchmarks/app/asgi.py +++ b/benchmarks/app/asgi.py @@ -1,3 +1,4 @@ +import asyncio import pathlib import sys @@ -84,6 +85,19 @@ async def file_pathsend(scope, receive, send): await send({'type': 'http.response.pathsend', 'path': str(MEDIA_PATH)}) +def io_builder(wait): + wait = wait / 1000 + async def io(scope, receive, send): + await send(PLAINTEXT_RESPONSE) + await asyncio.sleep(wait) + await send({ + 'type': 'http.response.body', + 'body': BODY_BYTES_SHORT, + 'more_body': False + }) + return io + + async def handle_404(scope, receive, send): content = b'Not found' await send(PLAINTEXT_RESPONSE) @@ -102,6 +116,8 @@ async def handle_404(scope, receive, send): '/echo': echo, '/fb': file_body, '/fp': file_pathsend, + '/io10': io_builder(10), + '/io100': io_builder(100), } diff --git a/benchmarks/app/rsgi.py b/benchmarks/app/rsgi.py index 658620d8..82bfe010 100644 --- a/benchmarks/app/rsgi.py +++ b/benchmarks/app/rsgi.py @@ -1,3 +1,4 @@ +import asyncio import pathlib import sys @@ -60,6 +61,18 @@ async def file(scope, proto): ) +def io_builder(wait): + wait = wait / 1000 + async def io(scope, proto): + await asyncio.sleep(wait) + proto.response_bytes( + 200, + HEADERS, + BODY_BYTES_SHORT + ) + return io + + async def handle_404(scope, proto): proto.response_str( 404, @@ -75,6 +88,8 @@ async def handle_404(scope, proto): '/ss': s_long, '/echo': echo, '/fp': file, + '/io10': io_builder(10), + '/io100': io_builder(100), } diff --git a/benchmarks/app/wsgi.py b/benchmarks/app/wsgi.py index eb83f6a3..98c1330b 100644 --- a/benchmarks/app/wsgi.py +++ b/benchmarks/app/wsgi.py @@ -1,3 +1,5 @@ +import time + HEADERS = [('content-type', 'text/plain; charset=utf-8')] BODY_BYTES_SHORT = b"Test" @@ -31,6 +33,15 @@ def echo(environ, proto): return [environ['wsgi.input'].read()] +def io_builder(wait): + wait = wait / 1000 + def io(environ, proto): + proto('200 OK', HEADERS) + time.sleep(wait) + return [BODY_BYTES_SHORT] + return io + + def handle_404(environ, proto): proto('404 NOT FOUND', HEADERS) return [b"not found"] @@ -41,7 +52,9 @@ def handle_404(environ, proto): '/bb': b_long, '/s': s_short, '/ss': s_long, - '/echo': echo + '/echo': echo, + '/io10': io_builder(10), + '/io100': io_builder(100), } diff --git a/benchmarks/benchmarks.py b/benchmarks/benchmarks.py index 4b212dab..6c216ad0 100644 --- a/benchmarks/benchmarks.py +++ b/benchmarks/benchmarks.py @@ -263,6 +263,27 @@ def vs_files(): return results +def vs_io(): + results = {} + benches = {"10ms": ("io10", {}), "100ms": ("io100", {})} + for fw in [ + "granian_rsgi", + "granian_asgi", + "granian_wsgi", + "uvicorn_httptools", + "hypercorn", + "gunicorn_gevent", + "uwsgi", + ]: + for key, bench_data in benches.items(): + route, opts = bench_data + fw_app = fw.split("_")[1] if fw.startswith("granian") else fw + title = " ".join(item.title() for item in fw.split("_")) + with app(fw_app): + results[f"{title} {key}"] = benchmark(route, **opts) + return results + + def _granian_version(): import granian return granian.__version__ @@ -283,6 +304,7 @@ def run(): results["vs_wsgi"] = vs_wsgi() results["vs_http2"] = vs_http2() results["vs_files"] = vs_files() + results["vs_io"] = vs_io() with open("results/data.json", "w") as f: pyver = sys.version_info f.write(json.dumps({ diff --git a/benchmarks/templates/vs.md b/benchmarks/templates/vs.md index b4356530..11e9f2e7 100644 --- a/benchmarks/templates/vs.md +++ b/benchmarks/templates/vs.md @@ -28,3 +28,10 @@ Granian version: {{ =data.granian }} {{ _data = data.results["vs_files"] }} {{ include './_vs_table.tpl' }} + +### Long I/O + +Plain text 4 bytes response comparison simulating *long* I/O waits (10ms and 100ms). + +{{ _data = data.results["vs_io"] }} +{{ include './_vs_table.tpl' }}