Two endpoints for comparing async CPU work vs asyncio.to_thread offloading.
GET /api/async-normal/?iterations=5000000GET /api/async-to-thread/?iterations=5000000
Both return JSON including elapsed time and a dummy result.
GET /api/async-io/?duration_s=0.2— non-blocking I/O latency viaasyncio.sleepGET /api/async-io-to-thread/?duration_s=0.2— blockingtime.sleepoffloaded to a worker thread
Both return JSON with elapsed time and the input duration.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python manage.py migrate --noinputNote on Python 3.13 (macOS):
uvloopandgranianmay fail to build on Python 3.13 due to missing wheels.- This project marks them optional on Python ≥ 3.13. You can:
- Use plain
uvicorn(nouvloop/granian), or - Create a Python 3.12 virtualenv if you want to use
graniananduvloop.
- Use plain
uvicorn loadtest.asgi:application --host 0.0.0.0 --port 8000 --reloadgranian --interface asgi loadtest.asgi:application --host 0.0.0.0 --port 8000 --workers 4 --threads 1granian --interface wsgi loadtest.wsgi:application --host 0.0.0.0 --port 8000 --workers 4 --threads 8Then hit:
curl "http://127.0.0.1:8000/api/async-normal/?iterations=6000000"
curl "http://127.0.0.1:8000/api/async-to-thread/?iterations=6000000"
curl "http://127.0.0.1:8000/api/async-io/?duration_s=0.25"
curl "http://127.0.0.1:8000/api/async-io-to-thread/?duration_s=0.25"Use the helper to fire N requests with configurable concurrency against any URL:
chmod +x scripts/load_test.sh
./scripts/load_test.sh "http://127.0.0.1:8000/api/async-io/?duration_s=10" 200 20
./scripts/load_test.sh "http://127.0.0.1:8000/api/async-io-to-thread/?duration_s=10" 200 20