Python asyncio на практике: реальные замеры

Python — язык, наиболее близкий к PHP по модели исполнения: интерпретируемый, однопоточный (GIL), с доминированием синхронных фреймворков. Переход с синхронного Python (Flask, Django + Gunicorn) на асинхронный (FastAPI, aiohttp, Starlette + Uvicorn) — это точный аналог перехода с PHP-FPM на корутинный рантайм.

Ниже собраны production-кейсы, независимые бенчмарки и измерения.


1. Production: Duolingo — миграция на async Python (+40% throughput)

Duolingo — крупнейшая платформа для изучения языков (500M+ пользователей). Бэкенд написан на Python.

В 2025 году команда начала системную миграцию сервисов с синхронного Python на async.

МетрикаРезультат
Throughput на инстанс+40%
Экономия на AWS EC2~30% на каждый мигрированный сервис

Авторы отмечают, что после создания async-инфраструктуры миграция отдельных сервисов оказалась «достаточно простой» (fairly straightforward).

Источник: How We Started Our Async Python Migration (Duolingo Blog, 2025)


2. Production: Super.com — снижение затрат на 90%

Super.com (ранее Snaptravel) — сервис поиска отелей и скидок. Их поисковый движок обрабатывает 1 000+ req/s, принимает 1 TB+ данных в день и обрабатывает $1M+ продаж ежедневно.

Ключевая характеристика нагрузки: каждый запрос делает 40+ сетевых вызовов к сторонним API. Это чистый I/O-bound профиль — идеальный кандидат для корутин.

Команда мигрировала с Flask (синхронный, AWS Lambda) на Quart (ASGI, EC2).

МетрикаFlask (Lambda)Quart (ASGI)Изменение
Инфраструктурные затраты~$1 000/день~$50/день−90%
Throughput~150 req/s300+ req/s2x
Ошибки в пиковые часыBaseline−95%−95%
ЛатентностьBaseline−50%2x быстрее

Экономия $950/день × 365 = ~$350 000/год на одном сервисе.

Источник: How we optimized service performance using Quart ASGI and reduced costs by 90% (Super.com, Medium)


3. Production: Instagram — asyncio на масштабе 500M DAU

Instagram обслуживает 500+ миллионов активных пользователей ежедневно на Django-бэкенде.

Джимми Лай (инженер Instagram) описал миграцию на asyncio в докладе на PyCon Taiwan 2018:

  • Заменили requests на aiohttp для HTTP-вызовов
  • Перевели внутренний RPC на asyncio
  • Получили улучшение производительности API и снижение простоя CPU

Вызовы: высокий CPU-overhead asyncio на масштабе Instagram, необходимость автоматизированного поиска блокирующих вызовов через статический анализ кода.

Источник: The journey of asyncio adoption in Instagram (PyCon Taiwan 2018)


4. Production: Feature Store — с потоков на asyncio (−40% латентности)

Сервис Feature Store мигрировал с Python multithreading на asyncio.

МетрикаПотокиAsyncioИзменение
ЛатентностьBaseline−40%−40%
Потребление RAM18 GB (сотни потоков)Значительно меньшеСущественное снижение

Миграция проведена в три фазы с 50/50 разделением production-трафика для валидации.

Источник: How We Migrated from Python Multithreading to Asyncio (Medium)


5. Production: Talk Python — Flask → Quart (−81% латентности)

Talk Python — один из крупнейших Python-подкастов и обучающих платформ. Автор (Michael Kennedy) переписал сайт с Flask (синхронный) на Quart (асинхронный Flask).

МетрикаFlaskQuartИзменение
Время ответа (пример)42 ms8 ms−81%
Баги после миграции2Минимальные

Автор отмечает: при нагрузочном тестировании максимальный req/s отличался незначительно, потому что MongoDB-запросы занимали <1 ms. Выигрыш проявляется при конкурентной обработке запросов — когда несколько клиентов обращаются одновременно.

Источник: Talk Python rewritten in Quart (async Flask)


6. Microsoft Azure Functions — uvloop как стандарт

Microsoft включил uvloop — быстрый event loop на libuv — в качестве стандартного для Azure Functions на Python 3.13+.

ТестСтандартный asynciouvloopУлучшение
10K запросов, 50 VU (локально)515 req/s565 req/s+10%
5 мин, 100 VU (Azure)1 898 req/s1 961 req/s+3%
500 VU (локально)720 req/s772 req/s+7%

Стандартный event loop при 500 VU показал ~2% потерь запросов. uvloop — ноль ошибок.

Источник: Faster Python on Azure Functions with uvloop (Microsoft, 2025)


7. Бенчмарк: I/O-bound задачи — asyncio в 130 раз быстрее

Прямое сравнение моделей конкурентности на задаче загрузки 10 000 URL:

МодельВремяThroughputОшибки
Синхронная~1 800 с~11 KB/s
Потоки (100)~85 с~238 KB/sНизкий
Asyncio14 с1 435 KB/s0.06%

Asyncio: 130x быстрее синхронного кода, 6x быстрее потоков.

На CPU-bound задачах asyncio не даёт никакого преимущества (идентичное время, +44% потребления памяти).

Источник: Python Concurrency Model Comparison (Medium, 2025)


8. Бенчмарк: uvloop — быстрее Go и Node.js

uvloop — drop-in замена стандартного asyncio event loop, написанная на Cython поверх libuv (та же библиотека, что лежит в основе Node.js).

TCP echo-сервер:

Реализация1 KiB (req/s)100 KiB throughput
uvloop105 4592.3 GiB/s
Go103 264
Стандартный asyncio41 420
Node.js44 055

HTTP-сервер (300 concurrent):

Реализация1 KiB (req/s)
uvloop + httptools37 866
Node.jsНиже

uvloop: 2.5x быстрее стандартного asyncio, 2x быстрее Node.js, на уровне Go.

Источник: uvloop: Blazing fast Python networking (MagicStack)


9. Бенчмарк: aiohttp vs requests — 10x на конкурентных запросах

Библиотекаreq/s (конкурентно)Тип
aiohttp241+Async
HTTPX (async)~160Async
Requests~24Sync

aiohttp: 10x быстрее Requests при конкурентных HTTP-запросах.

Источник: HTTPX vs Requests vs AIOHTTP (Oxylabs)


10. Контр-аргумент: Cal Paterson — «Async Python не быстрее»

Важно привести и контр-аргументы. Cal Paterson провёл тщательный бенчмарк с реальной базой данных (PostgreSQL, случайная выборка строки + JSON):

ФреймворкТипreq/sP99 латентность
Gunicorn + Meinheld/BottleSync5 78032 ms
Gunicorn + Meinheld/FalconSync5 58931 ms
Uvicorn + StarletteAsync4 95275 ms
SanicAsync4 68785 ms
AIOHTTPAsync4 50176 ms

Результат: синхронные фреймворки с C-серверами показали выше throughput и в 2–3 раза лучше tail latency (P99).

Почему async проиграл?

Причины:

  1. Одиночный SQL-запрос на HTTP-запрос — слишком мало I/O, чтобы корутинная конкурентность дала эффект.
  2. Кооперативная многозадачность при CPU-работе между запросами создаёт «несправедливое» распределение CPU-времени — длинные вычисления блокируют event loop для всех.
  3. Overhead asyncio (стандартный event loop на Python) сопоставим с выигрышем от неблокирующего I/O при малом I/O.

Когда async действительно помогает

Бенчмарк Paterson'а тестирует простейший сценарий (1 SQL-запрос). Как показывают production-кейсы выше, async даёт радикальный выигрыш когда:

  • Запросов к БД / внешним API много (Super.com: 40+ вызовов на запрос)
  • Конкурентность высокая (тысячи одновременных соединений)
  • I/O доминирует над CPU (Duolingo, Appwrite)

Это совпадает с теорией: чем выше blocking coefficient (T_io/T_cpu), тем больше выигрыш от корутин. При 1 SQL-запросе × 2 ms коэффициент слишком мал.

Источник: Async Python is not faster (Cal Paterson)


11. TechEmpower: Python-фреймворки

Приблизительные результаты из TechEmpower Round 22:

ФреймворкТипreq/s (JSON)
Uvicorn (raw)Async ASGIМаксимум среди Python
StarletteAsync ASGI~20 000–25 000
FastAPIAsync ASGI~15 000–22 000
Flask (Gunicorn)Sync WSGI~4 000–6 000
Django (Gunicorn)Sync WSGI~2 000–4 000

Async-фреймворки: 3–5x быстрее синхронных в JSON-тесте.

Источник: TechEmpower Framework Benchmarks


Сводка: что показывают данные Python

КейсSync → AsyncУсловие
Duolingo (production)+40% throughput, −30% стоимостьМикросервисы, I/O
Super.com (production)2x throughput, −90% стоимость40+ API-вызовов на запрос
Feature Store (production)−40% латентностиМиграция с потоков на asyncio
Talk Python (production)−81% латентностиFlask → Quart
I/O-bound (10K URLs)130x быстрееЧистый I/O, массовая конкурентность
aiohttp vs requests10x быстрееКонкурентные HTTP-запросы
uvloop vs стандартный2.5x быстрееTCP echo, HTTP
TechEmpower JSON3–5xFastAPI/Starlette vs Flask/Django
Простой CRUD (1 SQL)Sync быстрееCal Paterson: P99 в 2–3x хуже у async
CPU-boundНет разницы+44% памяти, 0% выигрыша

Ключевой вывод

Async Python даёт максимальный выигрыш при высоком blocking coefficient: когда время I/O значительно превышает время CPU. При 40+ сетевых вызовах (Super.com) — экономия 90%. При 1 SQL-запросе (Cal Paterson) — async медленнее.

Это подтверждает формулу из Эффективность IO-bound задач: выигрыш ≈ 1 + T_io/T_cpu. При T_io >> T_cpu — десятки и сотни раз. При T_io ≈ T_cpu — минимальный или нулевой.


Связь с PHP и True Async

Python и PHP находятся в похожей ситуации:

ХарактеристикаPythonPHP
ИнтерпретируемыйДаДа
GIL / однопоточностьGILОднопоточный
Доминирующая модельSync (Django, Flask)Sync (FPM)
Async-рантаймasyncio + uvloopSwoole / True Async
Async-фреймворкFastAPI, StarletteHyperf

Данные Python показывают, что переход на корутины в однопоточном интерпретируемом языке — работает. Масштаб выигрыша определяется профилем нагрузки, а не языком.


Ссылки

Production-кейсы

Бенчмарки

Корутины vs потоки