|
| 1 | +""" |
| 2 | +Test the performance of simple HTTP serving with FastAPI. |
| 3 | +
|
| 4 | +This benchmark tests FastAPI's request handling, including: |
| 5 | +- Path parameter extraction and validation |
| 6 | +- Pydantic model serialization |
| 7 | +- JSON response encoding |
| 8 | +
|
| 9 | +The bench serves a REST API endpoint that returns JSON objects, |
| 10 | +simulating a typical web application scenario. |
| 11 | +
|
| 12 | +Author: Savannah Ostrowski |
| 13 | +""" |
| 14 | + |
| 15 | +import asyncio |
| 16 | +import socket |
| 17 | + |
| 18 | +import httpx |
| 19 | +import pyperf |
| 20 | +import threading |
| 21 | +import uvicorn |
| 22 | +from fastapi import FastAPI |
| 23 | +from pydantic import BaseModel |
| 24 | + |
| 25 | +HOST = "127.0.0.1" |
| 26 | + |
| 27 | +CONCURRENCY = 150 |
| 28 | + |
| 29 | +class Item(BaseModel): |
| 30 | + id: int |
| 31 | + name: str |
| 32 | + price: float |
| 33 | + tags: list[str] = [] |
| 34 | + |
| 35 | +app = FastAPI() |
| 36 | + |
| 37 | +@app.get("/items/{item_id}", response_model=Item) |
| 38 | +async def get_item(item_id: int): |
| 39 | + return { |
| 40 | + "id": item_id, |
| 41 | + "name": "Sample Item", |
| 42 | + "price": 9.99, |
| 43 | + "tags": ["sample", "item", "fastapi"] |
| 44 | + } |
| 45 | + |
| 46 | +def setup_server(): |
| 47 | + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: |
| 48 | + s.bind((HOST, 0)) |
| 49 | + s.listen(1) |
| 50 | + port = s.getsockname()[1] |
| 51 | + |
| 52 | + config = uvicorn.Config(app, host=HOST, port=port, log_level="error") |
| 53 | + server = uvicorn.Server(config) |
| 54 | + |
| 55 | + server_thread = threading.Thread(target=server.run, daemon=True) |
| 56 | + server_thread.start() |
| 57 | + |
| 58 | + while not server.started: |
| 59 | + pass |
| 60 | + |
| 61 | + url = f"http://{HOST}:{port}" |
| 62 | + return url |
| 63 | + |
| 64 | +def bench_fastapi(loops, url): |
| 65 | + async def run_benchmark(): |
| 66 | + async with httpx.AsyncClient() as client: |
| 67 | + t0 = pyperf.perf_counter() |
| 68 | + |
| 69 | + for i in range(loops): |
| 70 | + tasks = [ |
| 71 | + client.get(f"{url}/items/{i}") |
| 72 | + for _ in range(CONCURRENCY) |
| 73 | + ] |
| 74 | + responses = await asyncio.gather(*tasks) |
| 75 | + for response in responses: |
| 76 | + response.raise_for_status() |
| 77 | + data = response.json() |
| 78 | + assert data["id"] == i |
| 79 | + assert "tags" in data |
| 80 | + |
| 81 | + return pyperf.perf_counter() - t0 |
| 82 | + |
| 83 | + return asyncio.run(run_benchmark()) |
| 84 | + |
| 85 | + |
| 86 | +if __name__ == "__main__": |
| 87 | + url = setup_server() |
| 88 | + runner = pyperf.Runner() |
| 89 | + runner.metadata['description'] = "Test the performance of HTTP requests with FastAPI" |
| 90 | + runner.bench_time_func("fastapi_http", bench_fastapi, url) |
0 commit comments