Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
I/O-bound vs CPU-bound задачи: Различия и оптимизация
Это одна из ключевых концепций, которая определяет оптимизацию кода. Большинство разработчиков это путают.
Основные различия
I/O-bound задачи — время потрачено на ввод-вывод:
- Чтение с диска (disk I/O)
- Запросы в БД (network latency)
- HTTP запросы к API
- Чтение файлов
CPU-bound задачи — время потрачено на обработку:
- Математические вычисления
- Компрессия данных
- Parsing больших JSON
- Машинное обучение (ML inference)
Пример: Fetch vs Compute
import time
import requests
# I/O-bound: ждём API
def fetch_sequential():
start = time.time()
for i in range(100):
requests.get('https://api.example.com/data') # 200ms каждый
# Total: ~20 seconds (process ждёт)
return time.time() - start
# CPU-bound: считаем
def compute_expensive():
start = time.time()
result = sum(i**2 for i in range(100_000_000))
# Total: ~5 seconds (CPU работает)
return time.time() - start
Оптимизация I/O-bound
Threading:
from concurrent.futures import ThreadPoolExecutor
# Sequential: 20 seconds
for url in urls:
requests.get(url)
# Parallel: 2 seconds (10 threads)
with ThreadPoolExecutor(max_workers=10) as executor:
results = executor.map(requests.get, urls)
# 10x faster! Потому что thread может работать, пока другой ждёт I/O
Async/await (лучше):
import asyncio
import aiohttp
async def fetch_async():
async with aiohttp.ClientSession() as session:
tasks = [session.get(url) for url in urls]
await asyncio.gather(*tasks) # ~0.2 seconds
# Async > Threading для I/O:
# - Меньше memory (корутины vs потоки)
# - Нет GIL проблем
# - Быстрее переключение контекста
Connection pooling (для БД):
from sqlalchemy import create_engine
# Без pooling: каждый запрос = новое соединение
engine_bad = create_engine('postgresql://...')
# С pooling: переиспользуй соединения
engine_good = create_engine(
'postgresql://...',
pool_size=20, # Держи 20 соединений
max_overflow=40
)
# 1000 запросов:
# - Без pooling: 50 seconds
# - С pooling: 5 seconds
Оптимизация CPU-bound
Multiprocessing (обходит GIL):
from multiprocessing import Pool
import os
def expensive_calc(data):
return sum(i**2 for i in range(10_000_000))
# Threading НЕ поможет (GIL блокирует)
with ThreadPoolExecutor() as e:
results = list(e.map(expensive_calc, chunks)) # ~20 seconds
# Multiprocessing ПОМОЖЕТ (разные процессы = разные GILs)
with Pool(os.cpu_count()) as pool:
results = pool.map(expensive_calc, chunks) # ~2.5 seconds (8 cores)
NumPy Vectorization:
import numpy as np
data = list(range(10_000_000))
# Python loop: 1.5 seconds
result = [x**2 for x in data]
# NumPy: 0.05 seconds
result = np.array(data) ** 2
# 30x faster! NumPy работает с C-уровнем
Numba JIT:
from numba import jit
@jit(nopython=True)
def sum_squares():
result = 0
for i in range(100_000_000):
result += i**2
return result
# Python: 5 seconds
# Numba: 0.1 seconds (compiled to machine code)
Матрица решений
| Тип задачи | Проблема | Решение | Ускорение |
|---|---|---|---|
| I/O (API) | Network wait | async/await | 10-100x |
| I/O (БД) | Connection setup | pooling | 5-10x |
| I/O (диск) | Disk latency | threading | 5x |
| CPU | GIL блокирует | multiprocessing | 2-8x |
| CPU (циклы) | Медленно | NumPy | 10-100x |
| CPU (вычисления) | Медленно | Numba | 10-100x |
Как узнать тип задачи
import cProfile
cProfile.run('my_function()')
# Если профайл показывает:
# requests.get, psycopg2, socket.recv → I/O-bound
# Используй: async, threading, pooling
# Если профайл показывает:
# for loops, numpy, calculations → CPU-bound
# Используй: multiprocessing, NumPy, Numba
Реальный пример: ETL Pipeline
class DataPipeline:
async def run(self):
# Step 1: Fetch 1000 URLs (I/O-bound)
# Решение: async → 1 second
data = await self.fetch_urls_async()
# Step 2: Transform данные (CPU-bound)
# Решение: multiprocessing → 5 seconds
transformed = self.transform_parallel(data)
# Step 3: Load в БД (I/O-bound)
# Решение: connection pooling → 2 seconds
self.load_batch(transformed)
# Total: 8 seconds
# vs Sequential: ~50 seconds (6x faster)
Ключевой вывод
I/O-bound: код ждёт внешних систем → используй async/threading
CPU-bound: код считает → используй multiprocessing/NumPy
Выбор неправильного инструмента = 0 улучшения. Выбор правильного = 10-100x ускорение.