From 4f50d0053e9f62e520decc0eeec48806468f5dd2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 15 Nov 2024 09:05:12 -0600 Subject: [PATCH 1/3] Add benchmarks for the URL dispatcher for checking https://github.com/aio-libs/aiohttp/pull/9907 impact --- tests/test_benchmarks_web_urldispatcher.py | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 tests/test_benchmarks_web_urldispatcher.py diff --git a/tests/test_benchmarks_web_urldispatcher.py b/tests/test_benchmarks_web_urldispatcher.py new file mode 100644 index 00000000000..916623bdafa --- /dev/null +++ b/tests/test_benchmarks_web_urldispatcher.py @@ -0,0 +1,133 @@ +"""codspeed benchmarks for the URL dispatcher.""" + +import asyncio +from typing import NoReturn +from unittest import mock + +from multidict import CIMultiDict, CIMultiDictProxy +from pytest_codspeed import BenchmarkFixture +from yarl import URL + +from aiohttp import web +from aiohttp.http import HttpVersion, RawRequestMessage + + +def _mock_request(method: str, path: str) -> web.Request: + message = RawRequestMessage( + method, + path, + HttpVersion(1, 1), + CIMultiDictProxy(CIMultiDict()), + (), + False, + None, + False, + False, + URL(path), + ) + + return web.Request( + message, mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock() + ) + + +def test_resolve_root_route( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve top level PlainResources route 100 times.""" + resolve_count = 100 + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + app.router.add_route("GET", "/", handler) + request = _mock_request(method="GET", path="/") + + async def run_url_dispatcher_benchmark() -> None: + for _ in range(resolve_count): + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) + + +def test_resolve_single_fixed_url_with_many_routes( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve PlainResources route 100 times.""" + resolve_count = 100 + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + for count in range(250): + app.router.add_route("GET", f"/api/server/dispatch/{count}/update", handler) + request = _mock_request(method="GET", path="/api/server/dispatch/1/update") + + async def run_url_dispatcher_benchmark() -> None: + for _ in range(resolve_count): + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) + + +def test_resolve_multiple_fixed_url_with_many_routes( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve 250 different PlainResources routes.""" + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + for count in range(250): + app.router.add_route("GET", f"/api/server/dispatch/{count}/update", handler) + + requests = [ + _mock_request(method="GET", path=f"/api/server/dispatch/{count}/update") + for count in range(250) + ] + + async def run_url_dispatcher_benchmark() -> None: + for request in requests: + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) + + +def test_resolve_dynamic_resource_url_with_many_routes( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve different a DynamicResource when there are 250 PlainResources registered.""" + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + for count in range(250): + app.router.add_route("GET", f"/api/server/other/{count}/update", handler) + app.router.add_route("GET", "/api/server/dispatch/{customer}/update", handler) + + requests = [ + _mock_request(method="GET", path=f"/api/server/dispatch/{customer}/update") + for customer in range(250) + ] + + async def run_url_dispatcher_benchmark() -> None: + for request in requests: + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) From b2d0af3bd9c16d672703a1ff3a5757b692d0f94e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 15 Nov 2024 10:27:07 -0600 Subject: [PATCH 2/3] additional benchmarks --- tests/test_benchmarks_web_urldispatcher.py | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/test_benchmarks_web_urldispatcher.py b/tests/test_benchmarks_web_urldispatcher.py index 916623bdafa..cba969c4d71 100644 --- a/tests/test_benchmarks_web_urldispatcher.py +++ b/tests/test_benchmarks_web_urldispatcher.py @@ -1,6 +1,7 @@ """codspeed benchmarks for the URL dispatcher.""" import asyncio +import pathlib from typing import NoReturn from unittest import mock @@ -8,6 +9,7 @@ from pytest_codspeed import BenchmarkFixture from yarl import URL +import aiohttp from aiohttp import web from aiohttp.http import HttpVersion, RawRequestMessage @@ -54,6 +56,29 @@ def _run() -> None: loop.run_until_complete(run_url_dispatcher_benchmark()) +def test_resolve_static_root_route( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve top level StaticResource route 100 times.""" + resolve_count = 100 + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + app.router.add_static("/", pathlib.Path(aiohttp.__file__).parent) + request = _mock_request(method="GET", path="/") + + async def run_url_dispatcher_benchmark() -> None: + for _ in range(resolve_count): + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) + + def test_resolve_single_fixed_url_with_many_routes( loop: asyncio.AbstractEventLoop, benchmark: BenchmarkFixture, @@ -105,6 +130,38 @@ def _run() -> None: loop.run_until_complete(run_url_dispatcher_benchmark()) +def test_resolve_multiple_level_fixed_url_with_many_routes( + loop: asyncio.AbstractEventLoop, + benchmark: BenchmarkFixture, +) -> None: + """Resolve 1024 different PlainResources routes.""" + + async def handler(request: web.Request) -> NoReturn: + assert False + + app = web.Application() + urls = [ + f"/api/{a}/{b}/{c}/{d}/{e}/update" + for a in ("a", "b", "c", "d") + for b in ("e", "f", "g", "h") + for c in ("i", "j", "k", "l") + for d in ("m", "n", "o", "p") + for e in ("n", "o", "p", "q") + ] + for url in urls: + app.router.add_route("GET", url, handler) + + requests = [_mock_request(method="GET", path=url) for url in urls] + + async def run_url_dispatcher_benchmark() -> None: + for request in requests: + await app._router.resolve(request) + + @benchmark + def _run() -> None: + loop.run_until_complete(run_url_dispatcher_benchmark()) + + def test_resolve_dynamic_resource_url_with_many_routes( loop: asyncio.AbstractEventLoop, benchmark: BenchmarkFixture, From 3585ca7f290b5939d2e09f96433d849031edea68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 15 Nov 2024 10:30:18 -0600 Subject: [PATCH 3/3] preen --- tests/test_benchmarks_web_urldispatcher.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_benchmarks_web_urldispatcher.py b/tests/test_benchmarks_web_urldispatcher.py index cba969c4d71..2ffb53ee0f7 100644 --- a/tests/test_benchmarks_web_urldispatcher.py +++ b/tests/test_benchmarks_web_urldispatcher.py @@ -63,9 +63,6 @@ def test_resolve_static_root_route( """Resolve top level StaticResource route 100 times.""" resolve_count = 100 - async def handler(request: web.Request) -> NoReturn: - assert False - app = web.Application() app.router.add_static("/", pathlib.Path(aiohttp.__file__).parent) request = _mock_request(method="GET", path="/")