Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как писать высокопроизводительный код
Производительность — не магия, это система. Высокопроизводительный код пишется с самого начала, а не оптимизируется после. Рассмотрю систематический подход на примерах.
1. Профилирование первым делом
import cProfile
import pstats
from functools import wraps
import time
# Декоратор для быстрого профилирования
def profile_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f'{func.__name__}: {elapsed*1000:.2f}ms')
return result
return wrapper
# Полное профилирование
def profile_function(func):
pr = cProfile.Profile()
pr.enable()
func()
pr.disable()
ps = pstats.Stats(pr)
ps.sort_stats('cumulative')
ps.print_stats(10) # Топ 10 функций
# Memory профилирование
from memory_profiler import profile
@profile
def memory_intensive():
big_list = [i for i in range(10_000_000)]
return sum(big_list)
# Используй: python -m memory_profiler script.py
2. Алгоритмическая сложность первична
# ❌ O(n²) — медленно
def slow_duplicates(arr):
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] == arr[j]:
return True
return False
# ✅ O(n) — быстро
def fast_duplicates(arr):
seen = set()
for item in arr:
if item in seen:
return True
seen.add(item)
return False
# Тест
import time
arr = list(range(100_000))
start = time.perf_counter()
slow_duplicates(arr)
print(f'Slow: {(time.perf_counter()-start)*1000:.1f}ms') # ~5000ms
start = time.perf_counter()
fast_duplicates(arr)
print(f'Fast: {(time.perf_counter()-start)*1000:.1f}ms') # ~10ms
# Разница в 500 раз!
3. Структуры данных имеют значение
import timeit
# Поиск в list — O(n)
data_list = list(range(1_000_000))
time_list = timeit.timeit(lambda: 999_999 in data_list, number=10)
print(f'List search: {time_list*100:.2f}ms')
# Поиск в set — O(1)
data_set = set(range(1_000_000))
time_set = timeit.timeit(lambda: 999_999 in data_set, number=10)
print(f'Set search: {time_set*100:.2f}ms')
# Разница в 1000x раз!
# Правильный выбор структур данных:
data_structures = {
'list': {
'access': 'O(1)',
'insert': 'O(n)',
'search': 'O(n)',
'use_when': 'Нужны индексы и порядок'
},
'dict': {
'access': 'O(1)',
'insert': 'O(1)',
'search': 'O(1)',
'use_when': 'Ключ-значение поиск'
},
'set': {
'access': 'no',
'insert': 'O(1)',
'search': 'O(1)',
'use_when': 'Уникальные значения, поиск'
},
'heap': {
'access': 'O(1) min',
'insert': 'O(log n)',
'search': 'O(n)',
'use_when': 'Priority queue'
}
}
4. Избегай ненужной работы
# ❌ Создаёт список из 1 млн элементов
big_range = list(range(1_000_000))
for i in big_range:
print(i)
# ✅ Генератор — O(1) памяти
for i in range(1_000_000):
print(i)
# ❌ Копирует список каждый раз
def slow():
result = []
for i in range(1000):
new_list = result + [i] # Копирует весь list!
result = new_list
return result
# ✅ Append модифицирует на месте
def fast():
result = []
for i in range(1000):
result.append(i) # O(1) в амортизированном
return result
print(timeit.timeit(slow, number=100)) # ~200ms
print(timeit.timeit(fast, number=100)) # ~5ms
5. Кеширование и мемоизация
from functools import lru_cache
import time
# Медленная функция (без кеша)
def slow_fib(n):
if n < 2:
return n
time.sleep(0.001) # Имитация сложного вычисления
return slow_fib(n-1) + slow_fib(n-2)
# С кешем
@lru_cache(maxsize=128)
def fast_fib(n):
if n < 2:
return n
time.sleep(0.001)
return fast_fib(n-1) + fast_fib(n-2)
# Для custom кеша
from functools import cache
@cache # Python 3.9+, не требует maxsize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Кеш в production с Redis
import redis
from json import dumps, loads
class RedisCache:
def __init__(self):
self.redis = redis.Redis(host='localhost')
def cached(self, ttl=3600):
def decorator(func):
def wrapper(*args, **kwargs):
key = f'{func.__name__}:{args}:{kwargs}'
# Пытаемся получить из кеша
cached = self.redis.get(key)
if cached:
return loads(cached)
# Вычисляем и кешируем
result = func(*args, **kwargs)
self.redis.setex(key, ttl, dumps(result))
return result
return wrapper
return decorator
cache = RedisCache()
@cache.cached(ttl=3600)
def expensive_operation(x, y):
return x ** y
6. Батчинг и булк операции
import sqlite3
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
# ❌ Медленно: 10,000 отдельных INSERT
for i in range(10_000):
cursor.execute('INSERT INTO users VALUES (?, ?)', (i, f'user{i}'))
conn.commit() # ~5 сек
# ✅ Быстро: один bulk insert
data = [(i, f'user{i}') for i in range(10_000)]
cursor.executemany('INSERT INTO users VALUES (?, ?)', data)
conn.commit() # ~50ms (в 100 раз быстрее!)
# В Django ORM
from django.db import models
# ❌ Медленно
for i in range(10_000):
User.objects.create(name=f'user{i}')
# ✅ Быстро
users = [User(name=f'user{i}') for i in range(10_000)]
User.objects.bulk_create(users, batch_size=1000)
7. Ленивые вычисления и потоковая обработка
# ❌ Загружает всё в памяти
def process_large_file(filename):
data = []
with open(filename) as f:
for line in f:
data.append(line.strip())
return [process(line) for line in data] # Весь файл в памяти
# ✅ Потоковая обработка
def process_large_file(filename):
with open(filename) as f:
for line in f:
yield process(line.strip()) # O(1) памяти
# Для параллельной обработки
from multiprocessing import Pool
def parallel_process(items):
with Pool(4) as pool: # 4 процесса
results = pool.map(expensive_function, items)
return results
# Для IO операций
import asyncio
async def fetch_many(urls):
tasks = [fetch(url) for url in urls]
return await asyncio.gather(*tasks) # Параллельные запросы
8. Оптимизация циклови списков
# ❌ Медленно: функция вызывается каждый раз
result = []
for i in range(1_000_000):
result.append(expensive_function(i))
# ✅ Быстро: list comprehension
result = [expensive_function(i) for i in range(1_000_000)]
# Ещё быстрее: без функции
result = [i * 2 for i in range(1_000_000)]
# Сравнение скорости
import timeit
f1 = timeit.timeit(lambda: [x*2 for x in range(10_000)], number=100)
f2 = timeit.timeit(lambda: [expensive_function(x) for x in range(10_000)], number=100)
f3 = timeit.timeit(lambda: list(map(expensive_function, range(10_000))), number=100)
print(f'List comp: {f1:.3f}s')
print(f'List comp с функцией: {f2:.3f}s')
print(f'Map: {f3:.3f}s')
# List comprehension обычно быстрее!
9. Использование NumPy для векторизации
import numpy as np
# ❌ Медленный Python цикл
def slow_sum(n):
total = 0
for i in range(n):
total += i
return total
# ✅ NumPy (в 100+ раз быстрее)
def fast_sum(n):
return np.arange(n).sum()
n = 10_000_000
import timeit
print(timeit.timeit(lambda: slow_sum(n), number=1)) # ~500ms
print(timeit.timeit(lambda: fast_sum(n), number=1)) # ~5ms
# Когда использовать NumPy:
use_numpy = {
'mathematical_operations': True,
'large_arrays': True,
'matrix_operations': True,
'use_case': 'data processing, machine learning'
}
10. Чеклист высокопроизводительного кода
☐ ПРОФИЛИРУЙ! Сначала узнай где узкие места
☐ Алгоритмическая сложность O(n) лучше чем O(n²)
☐ Выбирай правильные структуры данных (set vs list)
☐ Избегай копирований (используй generator, slice views)
☐ Кешируй результаты (lru_cache, Redis)
☐ Батчинг: 10,000 x по 1 медленнее чем 1 x 10,000
☐ Ленивые вычисления (генераторы, потоковая обработка)
☐ List comprehension быстрее чем цикл
☐ NumPy для численных данных
☐ Multiprocessing для CPU-bound
☐ Asyncio для IO-bound
☐ Минимизируй исключения (дорогие)
☐ Избегай глобальных переменных
☐ Используй slots для больших объектов
☐ Компилируй критичный код (Cython, Numba)
11. Практичный пример: Оптимизация реального кода
# ДО: медленный код
def count_common_words(doc1, doc2):
words1 = doc1.split()
words2 = doc2.split()
common = 0
for word1 in words1:
for word2 in words2: # O(n²) !
if word1 == word2:
common += 1
return common
# ПОСЛЕ: быстрый код
def count_common_words_fast(doc1, doc2):
set1 = set(doc1.split()) # O(n)
set2 = set(doc2.split()) # O(n)
return len(set1 & set2) # O(n) пересечение
# Улучшение на 1000x для больших документов!
12. Спецификация производительности (SLA)
def performance_spec(endpoint, qps, latency_ms, concurrency):
"""
Определяй требования ДО начала разработки
"""
spec = {
'endpoint': endpoint,
'target_qps': qps,
'p50_latency_ms': latency_ms,
'p99_latency_ms': latency_ms * 5,
'concurrent_users': concurrency,
'error_rate': '< 0.1%'
}
# Пример
spec = performance_spec(
endpoint='GET /api/users',
qps=1000,
latency_ms=50,
concurrency=100
)
# Используй это для нагрузного тестирования
# locust, k6, Apache JMeter
# Нагрузное тестирование
from locust import HttpUser, task, between
class UserBehavior(HttpUser):
wait_time = between(1, 5)
@task
def get_users(self):
self.client.get('/api/users')
Мои рекомендации
- Профилируй первым — не гадай, измеряй
- Big O notation важна — O(n) vs O(n²) разница в миллионы раз
- Выбирай структуры данных правильно — set vs list это 1000x
- Батчинг > индивидуальные операции — всегда
- Кеширование это твой друг — но будь осторожен с инвалидацией
- Генераторы вместо list — когда можешь
- Не оптимизируй ранновремя — сначала пиши понятный код
- Но проектируй с учётом перформанса — архитектура важна
- Тестируй нагрузку — не угадаешь
- Monitoring в production — иначе не узнаешь что медленно