From e2a459e03f21211b8708a01d53082a1af76f7112 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 5 Nov 2025 19:40:33 -0800 Subject: [PATCH 1/5] Add FastAPI HTTP request benchmark --- pyperformance/data-files/benchmarks/MANIFEST | 1 + .../benchmarks/bm_fastapi/pyproject.toml | 15 ++++ .../benchmarks/bm_fastapi/requirements.txt | 17 ++++ .../benchmarks/bm_fastapi/run_benchmark.py | 88 +++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 pyperformance/data-files/benchmarks/bm_fastapi/pyproject.toml create mode 100644 pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt create mode 100644 pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py diff --git a/pyperformance/data-files/benchmarks/MANIFEST b/pyperformance/data-files/benchmarks/MANIFEST index 570d45aa..4d19dc3d 100644 --- a/pyperformance/data-files/benchmarks/MANIFEST +++ b/pyperformance/data-files/benchmarks/MANIFEST @@ -42,6 +42,7 @@ django_template dulwich_log docutils fannkuch +fastapi float genshi go diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/pyproject.toml b/pyperformance/data-files/benchmarks/bm_fastapi/pyproject.toml new file mode 100644 index 00000000..f8dd68d9 --- /dev/null +++ b/pyperformance/data-files/benchmarks/bm_fastapi/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "pyperformance_bm_fastapi" +requires-python = ">=3.10" +dependencies = [ + "pyperf", + "fastapi", + "uvicorn", + "httpx", +] +urls = {repository = "https://github.com/python/pyperformance"} +dynamic = ["version"] + +[tool.pyperformance] +name = "fastapi" +tags = "apps" diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt b/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt new file mode 100644 index 00000000..7ee4162e --- /dev/null +++ b/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt @@ -0,0 +1,17 @@ +annotated-doc==0.0.3 +annotated-types==0.7.0 +anyio==4.11.0 +certifi==2025.10.5 +click==8.3.0 +fastapi==0.121.0 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.11 +pydantic==2.12.4 +pydantic-core==2.41.5 +sniffio==1.3.1 +starlette==0.49.3 +typing-extensions==4.15.0 +typing-inspection==0.4.2 +uvicorn==0.38.0 diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py new file mode 100644 index 00000000..659f1985 --- /dev/null +++ b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py @@ -0,0 +1,88 @@ +""" +Test the performance of simple HTTP serving with FastAPI. + +This benchmark tests FastAPI's request handling, including: +- Path parameter extraction and validation +- Pydantic model serialization +- JSON response encoding + +The bench serves a REST API endpoint that returns JSON objects, +simulating a typical web application scenario. + +Author: Savannah Ostrowski +""" + +import asyncio +import socket +import time +from typing import List + +import httpx +import pyperf +import uvicorn +from fastapi import FastAPI +from pydantic import BaseModel + +HOST = "127.0.0.1" + +CONCURRENCY = 150 + +class Item(BaseModel): + id: int + name: str + price: float + tags: List[str] = [] + +app = FastAPI() + +@app.get("/items/{item_id}", response_model=Item) +async def get_item(item_id: int): + return { + "id": item_id, + "name": "Sample Item", + "price": 9.99, + "tags": ["sample", "item", "fastapi"] + } + + +async def run_server_and_benchmark(loops): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((HOST, 0)) + s.listen(1) + port = s.getsockname()[1] + + config = uvicorn.Config(app, host=HOST, port=port, log_level="error") + server = uvicorn.Server(config) + server_task = asyncio.create_task(server.serve()) + + await asyncio.sleep(0.5) + + async with httpx.AsyncClient() as client: + t0 = time.perf_counter() + + for i in range(loops): + tasks = [ + client.get(f"http://{HOST}:{port}/items/{i}") + for _ in range(CONCURRENCY) + ] + responses = await asyncio.gather(*tasks) + for response in responses: + response.raise_for_status() + data = response.json() + assert data["id"] == i + assert "tags" in data + + elapsed = time.perf_counter() - t0 + + server.should_exit = True + await server_task + return elapsed + +def bench_fastapi(loops): + return asyncio.run(run_server_and_benchmark(loops)) + + +if __name__ == "__main__": + runner = pyperf.Runner() + runner.metadata['description'] = "Test the performance of HTTP requests with FastAPI" + runner.bench_time_func("fastapi_http", bench_fastapi) \ No newline at end of file From 3cef6be2404a4b9664946ab62f058e576a734806 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 5 Nov 2025 19:41:06 -0800 Subject: [PATCH 2/5] Add new line --- pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py index 659f1985..66a641d9 100644 --- a/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py +++ b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py @@ -85,4 +85,4 @@ def bench_fastapi(loops): if __name__ == "__main__": runner = pyperf.Runner() runner.metadata['description'] = "Test the performance of HTTP requests with FastAPI" - runner.bench_time_func("fastapi_http", bench_fastapi) \ No newline at end of file + runner.bench_time_func("fastapi_http", bench_fastapi) From 1a172af356f492525afe85d8c91bc92fa7373148 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 5 Nov 2025 19:54:56 -0800 Subject: [PATCH 3/5] Use tabs in MANIFEST --- pyperformance/data-files/benchmarks/MANIFEST | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyperformance/data-files/benchmarks/MANIFEST b/pyperformance/data-files/benchmarks/MANIFEST index 4d19dc3d..dd22b77a 100644 --- a/pyperformance/data-files/benchmarks/MANIFEST +++ b/pyperformance/data-files/benchmarks/MANIFEST @@ -42,7 +42,7 @@ django_template dulwich_log docutils fannkuch -fastapi +fastapi float genshi go From 6f5bcfe0cac014b6c36b541f19bfccde5804b1c9 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 5 Nov 2025 20:05:33 -0800 Subject: [PATCH 4/5] Remove extraneous pinned deps --- .../benchmarks/bm_fastapi/requirements.txt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt b/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt index 7ee4162e..bbcf5238 100644 --- a/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt +++ b/pyperformance/data-files/benchmarks/bm_fastapi/requirements.txt @@ -1,17 +1,3 @@ -annotated-doc==0.0.3 -annotated-types==0.7.0 -anyio==4.11.0 -certifi==2025.10.5 -click==8.3.0 fastapi==0.121.0 -h11==0.16.0 -httpcore==1.0.9 httpx==0.28.1 -idna==3.11 -pydantic==2.12.4 -pydantic-core==2.41.5 -sniffio==1.3.1 -starlette==0.49.3 -typing-extensions==4.15.0 -typing-inspection==0.4.2 uvicorn==0.38.0 From 556e08f726881777d82978ae04d421fc54b43ed6 Mon Sep 17 00:00:00 2001 From: Savannah Ostrowski Date: Wed, 5 Nov 2025 20:08:45 -0800 Subject: [PATCH 5/5] Syntax cleanup --- .../data-files/benchmarks/bm_fastapi/run_benchmark.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py index 66a641d9..2c8bad5f 100644 --- a/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py +++ b/pyperformance/data-files/benchmarks/bm_fastapi/run_benchmark.py @@ -15,7 +15,6 @@ import asyncio import socket import time -from typing import List import httpx import pyperf @@ -31,7 +30,7 @@ class Item(BaseModel): id: int name: str price: float - tags: List[str] = [] + tags: list[str] = [] app = FastAPI()