← Назад к вопросам

Приведи пример когда асинхронность работает медленно

1.8 Middle🔥 161 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ: Когда asyncio медленнее, чем синхронный код

Асинхронность не всегда быстрее! Есть несколько сценариев, когда async код может быть медленнее обычного синхронного. Приведу примеры.

Пример 1: Один быстрый I/O запрос

Сценарий

Нужно сделать один API запрос к очень быстрому сервису (локальный, в одной сети).

Синхронный код

import requests
import time

def sync_single_request():
    start = time.time()
    response = requests.get("http://localhost:8000/api/fast")
    return time.time() - start

# Результат: ~0.01 сек
print(f"Sync: {sync_single_request():.4f}s")

Асинхронный код

import aiohttp
import asyncio
import time

async def async_single_request():
    start = time.time()
    async with aiohttp.ClientSession() as session:
        async with session.get("http://localhost:8000/api/fast") as response:
            await response.text()
    return time.time() - start

start = time.time()
asyncio.run(async_single_request())
print(f"Async: {time.time() - start:.4f}s")

Результаты

Sync:  0.0012 сек  ✓ быстрее
Async: 0.0145 сек  ✗ медленнее

Async медленнее на 12x!

Почему?

Overhead асинхронности:

asyncio.run():             ~2-3 мс (запуск event loop)
ClientSession создание:    ~3-4 мс (инициализация)
Event loop переключения:   ~1-2 мс
Сам запрос:                ~1 мс
────────────────────────────────
Всего:                     ~7-10 мс

У синхронного requests просто ~1 мс на запрос

Вывод: если только один быстрый запрос — async медленнее.

Пример 2: Много малых задач

Сценарий

Нужно обработать 100 задач, каждая занимает 1 миллисекунду.

Синхронный код

import time

def process_item(i):
    # Быстрая работа
    time.sleep(0.001)  # 1 мс
    return i * 2

def sync_process_many():
    start = time.time()
    results = [process_item(i) for i in range(100)]
    return time.time() - start

print(f"Sync: {sync_process_many():.4f}s")  # ~0.1 сек

Асинхронный код

import asyncio
import time

async def async_process_item(i):
    await asyncio.sleep(0.001)  # 1 мс
    return i * 2

async def async_process_many():
    start = time.time()
    tasks = [async_process_item(i) for i in range(100)]
    results = await asyncio.gather(*tasks)
    return time.time() - start

start = time.time()
asyncio.run(async_process_many())
print(f"Async: {time.time() - start:.4f}s")  # ~0.15 сек

Результаты

Sync:  0.100 сек  ✓ быстрее
Async: 0.150 сек  ✗ медленнее

Async медленнее на 50%!

Почему?

На малых задачах overhead контекстных переключений больше, чем выигрыш:

Любой await — это переключение контекста (overhead)
Если задача очень маленькая (< 100 мкс):
  Overhead переключения > время самой работы
  Итого работы медленнее.

Пример 3: CPU-bound вместо async

Сценарий

Сделать вычисления и вернуть результат (CPU-bound, не I/O).

import asyncio
import time

# Синхронный CPU-bound
def cpu_work():
    return sum(i*i for i in range(10_000_000))

start = time.time()
result = cpu_work()
print(f"Sync CPU-bound: {time.time() - start:.4f}s")  # ~0.5 сек

# Асинхронный CPU-bound (нет смысла)
async def async_cpu_work():
    # await не поможет — это синхронное вычисление
    return sum(i*i for i in range(10_000_000))

start = time.time()
resyncio.run(async_cpu_work())
print(f"Async CPU-bound: {time.time() - start:.4f}s")  # ~0.5 сек

Результаты

Sync CPU-bound:  0.5000 сек
Async CPU-bound: 0.5100 сек  (даже немного медленнее из-за overhead)

Вывод

Async не помогает с CPU-bound задачами!

CPU-bound блокирует весь Event Loop:

async def bad_example():
    # Пока выполняется sum() — Event Loop заблокирован
    # Никакие другие корутины не могут работать
    result = sum(i*i for i in range(10_000_000))
    return result

async def main():
    # Эта корутина будет ждать 0.5 сек
    await bad_example()
    print("Done")  # Напечатается только через 0.5 сек

Пример 4: Неправильное использование asyncio

Плохой паттерн: Sequential вместо parallel

import asyncio
import aiohttp
import time

async def slow_sequential():
    start = time.time()
    async with aiohttp.ClientSession() as session:
        # ❌ Плохо — ждёшь каждый запрос полностью
        for i in range(5):
            async with session.get(f"https://api.example.com/{i}") as r:
                await r.text()  # ждём ответ
    return time.time() - start

# Результат: 5 сек (если каждый запрос 1 сек)
print(f"Sequential: {asyncio.run(slow_sequential()):.1f}s")

Правильный паттерн: Parallel

async def fast_parallel():
    start = time.time()
    async with aiohttp.ClientSession() as session:
        # ✓ Хорошо — все запросы параллельно
        tasks = [
            session.get(f"https://api.example.com/{i}")
            for i in range(5)
        ]
        responses = await asyncio.gather(*tasks)
        for r in responses:
            await r.text()
    return time.time() - start

# Результат: 1 сек (если каждый запрос 1 сек)
print(f"Parallel: {asyncio.run(fast_parallel()):.1f}s")

Результаты

Sequential: 5.0 сек  ✗ медленнее (неправильно)
Parallel:   1.0 сек  ✓ быстрее (правильно)

Пример 5: Контекстные переключения в цикле

Проблема

Осталось 4 месяца, но если очень часто yield контроль:

import asyncio
import time

async def many_context_switches():
    start = time.time()
    for i in range(10000):
        # Каждая итерация = контекстное переключение
        await asyncio.sleep(0)  # empty switch
    return time.time() - start

print(f"10k switches: {asyncio.run(many_context_switches()):.4f}s")
# Результат: ~0.15 сек (на самом деле это слишком медленно)

Сравнительная таблица

СценарийSyncAsyncВывод
1 быстрый запрос0.01s0.015sSync быстрее
100 I/O запросов в параллель100s1sAsync быстрее 10x
100 I/O запросов последовательно100s100sОдинаково
CPU-bound вычисления0.5s0.5sОдинаково
Смешано: CPU + I/ODependsМедленнее всегдаEvent Loop заблокирован
Очень много малых задач0.1s0.15sSync быстрее

Когда НЕ использовать async

1. Один-два I/O запроса

# ❌ Не использовать async
async def one_api_call():
    ...

# ✓ Использовать синхронный requests
response = requests.get(url)

2. CPU-bound задачи

# ❌ Не использовать async
async def cpu_bound():
    return sum(i*i for i in range(10_000_000))

# ✓ Использовать multiprocessing
from multiprocessing import Pool
with Pool() as p:
    result = p.apply(cpu_bound)

3. Очень много маленьких операций

# ❌ Не использовать async
for i in range(1000):
    await asyncio.sleep(0)  # не имеет смысла

# ✓ Использовать обычный цикл
for i in range(1000):
    process(i)

Правило использования async

Async полезен ТОЛЬКО когда:

1. Есть МНОГО I/O операций (>= 10)
2. I/O операции ДОЛГИЕ (>= 100 мс)
3. Операции могут выполняться ПАРАЛЛЕЛЬНО
4. Вы используете GATHER или GATHER_ALL (не последовательно)

Если нарушить хоть одно условие — async медленнее.

Итог

  • Async не магия — это имеет overhead
  • Async блокируется на CPU-bound задачах
  • Async неправильно используется в последовательных операциях
  • На одном-двух быстрых запросах sync быстрее
  • Async теряет время на контекстные переключения на малых задачах

Главное правило: использовать async для параллельных I/O операций, не для всего подряд. Инструмент должен соответствовать задаче.

Приведи пример когда асинхронность работает медленно | PrepBro