Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как затрачивается CPU в асинхронности?
Этот вопрос базируется на критичном непонимании: асинхронность не снижает потребление CPU для CPU-bound операций. Асинхронность эффективна только для I/O-bound операций.
Синхронное выполнение
import time
def fetch_data(url):
time.sleep(2) # Имитация сетевого запроса
return f"Data from {url}"
# Синхронный код
start = time.time()
result1 = fetch_data("api1.com")
result2 = fetch_data("api2.com")
result3 = fetch_data("api3.com")
print(f"Total time: {time.time() - start}s")
# Total time: 6.0s (2 + 2 + 2)
Процесс блокируется на каждый запрос. CPU не занят (ждёт I/O), но поток занят.
Асинхронное выполнение
import asyncio
async def fetch_data(url):
await asyncio.sleep(2) # Имитация сетевого запроса
return f"Data from {url}"
# Асинхронный код
async def main():
start = time.time()
result1, result2, result3 = await asyncio.gather(
fetch_data("api1.com"),
fetch_data("api2.com"),
fetch_data("api3.com"),
)
print(f"Total time: {time.time() - start}s")
# Total time: 2.0s (работают параллельно!)
asyncio.run(main())
Замечание: оба варианта затрачивают одинаковое количество CPU! Потребление CPU такое же, но асинхронность работает быстрее, так как не ждёт.
I/O-bound vs CPU-bound
I/O-bound (сетевые запросы, чтение файлов)
# СИНХРОННО
import requests
def sync_requests():
start = time.time()
for i in range(3):
response = requests.get("https://api.github.com") # Блокирует 1-2 сек
print(f"Time: {time.time() - start}s") # ~3 сек
# АСИНХРОННО
import aiohttp
import asyncio
async def async_requests():
async with aiohttp.ClientSession() as session:
start = time.time()
tasks = []
for i in range(3):
tasks.append(session.get("https://api.github.com")) # Не блокирует!
await asyncio.gather(*tasks)
print(f"Time: {time.time() - start}s") # ~1 сек
asyncio.run(async_requests())
Временная шкала:
Синхронно: [Request 1 ........][Request 2 ........][Request 3 ........] = 3 сек
Асинхронно: [Request 1 ....][Request 2 ....][Request 3 ....] = 1 сек (параллельно)
CPU затрачено: Одинаково! Но асинхронность работает 3x быстрее.
CPU-bound (вычисления, обработка данных)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# СИНХРОННО
import time
start = time.time()
for i in range(3):
result = fibonacci(35) # Тяжёлое вычисление
print(f"Time: {time.time() - start}s") # ~3 сек
print(f"CPU usage: 100%")
# АСИНХРОННО
import asyncio
async def async_fibonacci():
start = time.time()
tasks = [
asyncio.create_task(asyncio.to_thread(fibonacci, 35)),
asyncio.create_task(asyncio.to_thread(fibonacci, 35)),
asyncio.create_task(asyncio.to_thread(fibonacci, 35)),
]
await asyncio.gather(*tasks)
print(f"Time: {time.time() - start}s") # ~3 сек (одинаково!)
print(f"CPU usage: 100%")
asyncio.run(async_fibonacci())
CPU затрачено: Одинаково! Асинхронность не помогает для CPU-bound операций на одном процессе (одном ядре CPU).
Как затрачивается CPU в асинхронности
Event Loop (главный цикл событий)
Асинхронный код работает в одном потоке через Event Loop:
import asyncio
async def task1():
print("Task 1 start") # CPU: 0.1%
await asyncio.sleep(1) # CPU: 0% (Event Loop уходит в другую задачу)
print("Task 1 end") # CPU: 0.1%
async def task2():
print("Task 2 start") # CPU: 0.1%
await asyncio.sleep(1) # CPU: 0% (Event Loop уходит в другую задачу)
print("Task 2 end") # CPU: 0.1%
async def main():
await asyncio.gather(task1(), task2())
# Event Loop расписание:
# 1. task1.start() — CPU работает
# 2. task1.await sleep() — CPU отдыхает
# 3. task2.start() — CPU работает
# 4. task2.await sleep() — CPU отдыхает
# 5. task1.end() — CPU работает
# 6. task2.end() — CPU работает
# Всего: 2 сек (параллельно), CPU ~1%
Проблема GIL (Global Interpreter Lock) в Python
Python на одном процессе (one thread):
# GIL позволяет только ОДНОМУ потоку работать с Python объектами одновременно
# НЕПРАВИЛЬНО для CPU-bound — threading не поможет
import threading
import time
def cpu_bound_task():
total = 0
for i in range(100_000_000):
total += i
start = time.time()
threads = [
threading.Thread(target=cpu_bound_task),
threading.Thread(target=cpu_bound_task),
]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Time: {time.time() - start}s") # ~20 сек (медленнее чем синхронно!)
# ПРАВИЛЬНО для CPU-bound — используйте multiprocessing
import multiprocessing
start = time.time()
processes = [
multiprocessing.Process(target=cpu_bound_task),
multiprocessing.Process(target=cpu_bound_task),
]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Time: {time.time() - start}s") # ~10 сек (параллельно, 2 ядра)
Затрачивание CPU в асинхронности
I/O-bound операция
Асинхронно (asyncio):
Event Loop: [Task1.run] → [await I/O] → [Task2.run] → [await I/O] → [Task1.end]
CPU: [■■■■] [ ] [■■■■] [ ] [■■■■] ~5%
Time: 3 сек (параллельно)
CPU-bound операция
Одного потока (asyncio):
Event Loop: [Task1.compute] → [Task2.compute] → [Task1.compute]
CPU: [■■■■■■■■■■■] → [■■■■■■■■■■■] → [■■■■■■■■■■■] 100% одного ядра
Time: 3 сек (последовательно на одном ядре)
Мультипроцесс (multiprocessing):
Core 1: [Task1.compute ....]
Core 2: [Task2.compute ....]
CPU: [■■■■■■■■■■■][■■■■■■■■■■■] 100% обоих ядер
Time: 1.5 сек (параллельно на разных ядрах)
Выводы
- Асинхронность НЕ снижает CPU для CPU-bound операций
- Асинхронность ЭКОНОМИТ CPU для I/O-bound (освобождает thread/process для других операций)
- I/O-bound: Используйте asyncio (async/await)
- CPU-bound: Используйте multiprocessing (отдельные процессы, другие ядра)
- GIL — проблема Python для threading, не для asyncio
Асинхронность эффективна для I/O-wait, а не для CPU-bound вычисления.