Skip to content

Commit 04f3a5f

Browse files
authored
Parameterize some codspeed benchmarks by connection type (tcp vs ssl) (aio-libs#12823)
1 parent e7d755d commit 04f3a5f

5 files changed

Lines changed: 144 additions & 27 deletions

File tree

CHANGES/12823.misc.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Parameterized some codspeed benchmarks by connection type (SSL + TCP).
2+
Previously, benchmarks only ran for TCP connection type. -- by :user:`tarasko`.

docs/spelling_wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ cmd
7777
codecov
7878
codebase
7979
codec
80+
codspeed
8081
Codings
8182
committer
8283
committers

tests/test_benchmarks_client.py

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
"""codspeed benchmarks for HTTP client."""
22

33
import asyncio
4-
from collections.abc import Iterator
5-
from typing import TYPE_CHECKING, Any
4+
import ssl
5+
from collections.abc import Awaitable, Callable, Iterator
6+
from dataclasses import dataclass
7+
from typing import TYPE_CHECKING, Any, TypedDict
68

79
import pytest
810
from pytest_aiohttp import AiohttpClient, AiohttpServer
911
from yarl import URL
1012

1113
from aiohttp import hdrs, request, web
12-
from aiohttp.test_utils import TestServer
14+
from aiohttp.test_utils import TestClient, TestServer
1315

1416
if TYPE_CHECKING:
1517
from pytest_codspeed import BenchmarkFixture
@@ -18,6 +20,56 @@
1820
BenchmarkFixture = pytest_codspeed.BenchmarkFixture
1921

2022

23+
@pytest.fixture
24+
def aiohttp_client_sync(
25+
event_loop: asyncio.AbstractEventLoop,
26+
) -> Iterator[
27+
Callable[[web.Application], Awaitable[TestClient[web.Request, web.Application]]]
28+
]:
29+
clients = []
30+
31+
async def go(
32+
app: web.Application,
33+
*,
34+
server_kwargs: dict[str, Any] | None = None,
35+
) -> TestClient[web.Request, web.Application]:
36+
server = TestServer(app)
37+
client = TestClient(server)
38+
await server.start_server(**(server_kwargs or {}))
39+
await client.start_server()
40+
clients.append(client)
41+
return client
42+
43+
yield go
44+
45+
while clients:
46+
event_loop.run_until_complete(clients.pop().close())
47+
48+
49+
class _ConnArgs(TypedDict, total=False):
50+
ssl: ssl.SSLContext
51+
52+
53+
@dataclass(frozen=True)
54+
class ConnectionType:
55+
s_kwargs: _ConnArgs
56+
c_kwargs: _ConnArgs
57+
58+
59+
@pytest.fixture(params=("tcp", "ssl"), ids=("tcp", "ssl"))
60+
def conn_type(
61+
request: pytest.FixtureRequest,
62+
ssl_ctx: ssl.SSLContext,
63+
client_ssl_ctx: ssl.SSLContext,
64+
) -> ConnectionType:
65+
if request.param == "ssl":
66+
return ConnectionType(
67+
s_kwargs={"ssl": ssl_ctx},
68+
c_kwargs={"ssl": client_ssl_ctx},
69+
)
70+
return ConnectionType(s_kwargs={}, c_kwargs={})
71+
72+
2173
@pytest.fixture
2274
def aiohttp_server_sync(
2375
event_loop: asyncio.AbstractEventLoop,
@@ -45,8 +97,9 @@ async def go(
4597

4698
def test_one_hundred_simple_get_requests(
4799
event_loop: asyncio.AbstractEventLoop,
48-
aiohttp_client: AiohttpClient,
100+
aiohttp_client_sync: AiohttpClient,
49101
benchmark: BenchmarkFixture,
102+
conn_type: ConnectionType,
50103
) -> None:
51104
"""Benchmark 100 simple GET requests."""
52105
message_count = 100
@@ -58,9 +111,9 @@ async def handler(request: web.Request) -> web.Response:
58111
app.router.add_route("GET", "/", handler)
59112

60113
async def run_client_benchmark() -> None:
61-
client = await aiohttp_client(app)
114+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
62115
for _ in range(message_count):
63-
await client.get("/")
116+
await client.get("/", **conn_type.c_kwargs)
64117
await client.close()
65118

66119
@benchmark
@@ -70,7 +123,7 @@ def _run() -> None:
70123

71124
def test_one_hundred_simple_get_requests_alternating_clients(
72125
event_loop: asyncio.AbstractEventLoop,
73-
aiohttp_client: AiohttpClient,
126+
aiohttp_client_sync: AiohttpClient,
74127
benchmark: BenchmarkFixture,
75128
) -> None:
76129
"""Benchmark 100 simple GET requests with alternating clients."""
@@ -83,8 +136,8 @@ async def handler(request: web.Request) -> web.Response:
83136
app.router.add_route("GET", "/", handler)
84137

85138
async def run_client_benchmark() -> None:
86-
client1 = await aiohttp_client(app)
87-
client2 = await aiohttp_client(app)
139+
client1 = await aiohttp_client_sync(app)
140+
client2 = await aiohttp_client_sync(app)
88141
for i in range(message_count):
89142
if i % 2 == 0:
90143
await client1.get("/")
@@ -154,8 +207,9 @@ def _run() -> None:
154207

155208
def test_one_hundred_get_requests_with_1024_chunked_payload(
156209
event_loop: asyncio.AbstractEventLoop,
157-
aiohttp_client: AiohttpClient,
210+
aiohttp_client_sync: AiohttpClient,
158211
benchmark: BenchmarkFixture,
212+
conn_type: ConnectionType,
159213
) -> None:
160214
"""Benchmark 100 GET requests with a small payload of 1024 bytes."""
161215
message_count = 100
@@ -170,9 +224,9 @@ async def handler(request: web.Request) -> web.Response:
170224
app.router.add_route("GET", "/", handler)
171225

172226
async def run_client_benchmark() -> None:
173-
client = await aiohttp_client(app)
227+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
174228
for _ in range(message_count):
175-
resp = await client.get("/")
229+
resp = await client.get("/", **conn_type.c_kwargs)
176230
await resp.read()
177231
await client.close()
178232

@@ -212,8 +266,9 @@ def _run() -> None:
212266

213267
def test_one_hundred_get_requests_with_1mb_chunked_payload(
214268
event_loop: asyncio.AbstractEventLoop,
215-
aiohttp_client: AiohttpClient,
269+
aiohttp_client_sync: AiohttpClient,
216270
benchmark: BenchmarkFixture,
271+
conn_type: ConnectionType,
217272
) -> None:
218273
"""Benchmark 100 GET requests with a 1 MiB chunked payload using read."""
219274
message_count = 100
@@ -228,9 +283,9 @@ async def handler(request: web.Request) -> web.Response:
228283
app.router.add_route("GET", "/", handler)
229284

230285
async def run_client_benchmark() -> None:
231-
client = await aiohttp_client(app)
286+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
232287
for _ in range(message_count):
233-
resp = await client.get("/")
288+
resp = await client.get("/", **conn_type.c_kwargs)
234289
await resp.read()
235290
await client.close()
236291

tests/test_benchmarks_client_ws.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"""codspeed benchmarks for websocket client."""
22

33
import asyncio
4+
import ssl
45
from collections.abc import Awaitable, Callable, Iterator
5-
from typing import TYPE_CHECKING, Any
6+
from dataclasses import dataclass
7+
from typing import TYPE_CHECKING, Any, TypedDict
68

79
import pytest
810
from pytest_aiohttp import AiohttpClient
@@ -34,10 +36,12 @@ async def go(
3436
server_kwargs: dict[str, Any] | None = None,
3537
**kwargs: Any,
3638
) -> TestClient[web.Request, web.Application]:
37-
server_kwargs = server_kwargs or {}
39+
server_kwargs = dict(server_kwargs or {})
40+
server_ssl_context = server_kwargs.pop("ssl", None)
3841
server = TestServer(__param, **server_kwargs)
3942
client = aiohttp_client_cls(server, **kwargs)
4043

44+
await server.start_server(ssl=server_ssl_context)
4145
await client.start_server()
4246
clients.append(client)
4347
return client
@@ -48,6 +52,30 @@ async def go(
4852
event_loop.run_until_complete(clients.pop().close())
4953

5054

55+
class _ConnArgs(TypedDict, total=False):
56+
ssl: ssl.SSLContext
57+
58+
59+
@dataclass(frozen=True)
60+
class ConnectionType:
61+
s_kwargs: _ConnArgs
62+
c_kwargs: _ConnArgs
63+
64+
65+
@pytest.fixture(params=("tcp", "ssl"), ids=("tcp", "ssl"))
66+
def conn_type(
67+
request: pytest.FixtureRequest,
68+
ssl_ctx: ssl.SSLContext,
69+
client_ssl_ctx: ssl.SSLContext,
70+
) -> ConnectionType:
71+
if request.param == "ssl":
72+
return ConnectionType(
73+
s_kwargs={"ssl": ssl_ctx},
74+
c_kwargs={"ssl": client_ssl_ctx},
75+
)
76+
return ConnectionType(s_kwargs={}, c_kwargs={})
77+
78+
5179
def test_one_thousand_round_trip_websocket_text_messages(
5280
event_loop: asyncio.AbstractEventLoop,
5381
aiohttp_client_sync: AiohttpClient,
@@ -84,6 +112,7 @@ def test_one_thousand_round_trip_websocket_binary_messages(
84112
event_loop: asyncio.AbstractEventLoop,
85113
aiohttp_client_sync: AiohttpClient,
86114
benchmark: BenchmarkFixture,
115+
conn_type: ConnectionType,
87116
msg_size: int,
88117
) -> None:
89118
"""Benchmark round trip of 1000 WebSocket binary messages."""
@@ -102,8 +131,8 @@ async def handler(request: web.Request) -> web.WebSocketResponse:
102131
app.router.add_route("GET", "/", handler)
103132

104133
async def run_websocket_benchmark() -> None:
105-
client = await aiohttp_client_sync(app)
106-
resp = await client.ws_connect("/")
134+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
135+
resp = await client.ws_connect("/", **conn_type.c_kwargs)
107136
for _ in range(message_count):
108137
await resp.receive()
109138
await resp.close()

tests/test_benchmarks_web_fileresponse.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import asyncio
44
import pathlib
5+
import ssl
56
from collections.abc import Awaitable, Callable, Iterator
6-
from typing import TYPE_CHECKING, Any
7+
from dataclasses import dataclass
8+
from typing import TYPE_CHECKING, Any, TypedDict
79

810
import pytest
911
from multidict import CIMultiDict
@@ -35,10 +37,12 @@ async def go(
3537
server_kwargs: dict[str, Any] | None = None,
3638
**kwargs: Any,
3739
) -> TestClient[web.Request, web.Application]:
38-
server_kwargs = server_kwargs or {}
40+
server_kwargs = dict(server_kwargs or {})
41+
server_ssl_context = server_kwargs.pop("ssl", None)
3942
server = TestServer(__param, **server_kwargs)
4043
client = aiohttp_client_cls(server, **kwargs)
4144

45+
await server.start_server(ssl=server_ssl_context)
4246
await client.start_server()
4347
clients.append(client)
4448
return client
@@ -49,10 +53,35 @@ async def go(
4953
event_loop.run_until_complete(clients.pop().close())
5054

5155

56+
class _ConnArgs(TypedDict, total=False):
57+
ssl: ssl.SSLContext
58+
59+
60+
@dataclass(frozen=True)
61+
class ConnectionType:
62+
s_kwargs: _ConnArgs
63+
c_kwargs: _ConnArgs
64+
65+
66+
@pytest.fixture(params=("tcp", "ssl"), ids=("tcp", "ssl"))
67+
def conn_type(
68+
request: pytest.FixtureRequest,
69+
ssl_ctx: ssl.SSLContext,
70+
client_ssl_ctx: ssl.SSLContext,
71+
) -> ConnectionType:
72+
if request.param == "ssl":
73+
return ConnectionType(
74+
s_kwargs={"ssl": ssl_ctx},
75+
c_kwargs={"ssl": client_ssl_ctx},
76+
)
77+
return ConnectionType(s_kwargs={}, c_kwargs={})
78+
79+
5280
def test_simple_web_file_response(
5381
event_loop: asyncio.AbstractEventLoop,
54-
aiohttp_client: AiohttpClient,
82+
aiohttp_client_sync: AiohttpClient,
5583
benchmark: BenchmarkFixture,
84+
conn_type: ConnectionType,
5685
) -> None:
5786
"""Benchmark creating 100 simple web.FileResponse."""
5887
response_count = 100
@@ -65,9 +94,9 @@ async def handler(request: web.Request) -> web.FileResponse:
6594
app.router.add_route("GET", "/", handler)
6695

6796
async def run_file_response_benchmark() -> None:
68-
client = await aiohttp_client(app)
97+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
6998
for _ in range(response_count):
70-
await client.get("/")
99+
await client.get("/", **conn_type.c_kwargs)
71100
await client.close()
72101

73102
@benchmark
@@ -77,8 +106,9 @@ def _run() -> None:
77106

78107
def test_simple_web_file_sendfile_fallback_response(
79108
event_loop: asyncio.AbstractEventLoop,
80-
aiohttp_client: AiohttpClient,
109+
aiohttp_client_sync: AiohttpClient,
81110
benchmark: BenchmarkFixture,
111+
conn_type: ConnectionType,
82112
) -> None:
83113
"""Benchmark creating 100 simple web.FileResponse without sendfile."""
84114
response_count = 100
@@ -94,9 +124,9 @@ async def handler(request: web.Request) -> web.FileResponse:
94124
app.router.add_route("GET", "/", handler)
95125

96126
async def run_file_response_benchmark() -> None:
97-
client = await aiohttp_client(app)
127+
client = await aiohttp_client_sync(app, server_kwargs=conn_type.s_kwargs)
98128
for _ in range(response_count):
99-
await client.get("/")
129+
await client.get("/", **conn_type.c_kwargs)
100130
await client.close()
101131

102132
@benchmark

0 commit comments

Comments
 (0)