Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Профилирование кода: выявление узких мест
Лучший способ отличить медленный код — это не гадать, а измерить. Python предоставляет отличные инструменты профилирования.
1. Базовое измерение времени
Простой способ с time.time():
import time
def slow_function():
time.sleep(1)
return "result"
start = time.time()
result = slow_function()
end = time.time()
print(f"Время выполнения: {end - start:.4f} сек")
# Выход: Время выполнения: 1.0001 сек
Более удобно с декоратором:
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__}: {end - start:.4f} сек")
return result
return wrapper
@timer
def process_data(n):
return sum(range(n))
process_data(10000000)
# Выход: process_data: 0.5123 сек
2. Модуль timeit для микробенчмарков
Сравнение разных реализаций:
import timeit
# Подход 1: List comprehension
setup1 = "squares = [x**2 for x in range(1000)]"
# Подход 2: Цикл
setup2 = """
squares = []
for x in range(1000):
squares.append(x**2)
"""
time1 = timeit.timeit(setup1, number=10000)
time2 = timeit.timeit(setup2, number=10000)
print(f"List comprehension: {time1:.4f} сек")
print(f"Цикл с append: {time2:.4f} сек")
print(f"Ускорение: {time2/time1:.2f}x")
Из командной строки:
# Сравнить скорость разных подходов
python -m timeit "sum(range(1000))"
# 100000 loops, best of 5: 23.7 usec per loop
python -m timeit "result = 0; [result := result + i for i in range(1000)]"
# 10000 loops, best of 5: 45.2 usec per loop
3. cProfile: полный анализ функций
Найти самые медленные функции:
import cProfile
import pstats
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
def process():
result = 0
for i in range(10):
result += fibonacci(30)
return result
# Профилирование
profiler = cProfile.Profile()
profiler.enable()
process()
profiler.disable()
# Вывод результатов
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative') # Отсортировать по общему времени
stats.print_stats(10) # Топ-10 функций
Вывод:
ncalls tottime percall cumtime percall filename:lineno(function)
177160001 0.567 0.000 0.567 0.000 script.py:1(fibonacci)
1 0.000 0.000 0.873 0.873 script.py:7(process)
1 0.000 0.000 0.873 0.873 {built-in method exec}
Из командной строки:
python -m cProfile -s cumulative script.py | head -20
4. line_profiler для анализа по строкам
# pip install line-profiler
from line_profiler import LineProfiler
def slow_function():
result = 0
for i in range(1000000):
result += i ** 2 # Эта строка медленная
return result
lpr = LineProfiler()
lpr.add_function(slow_function)
lpr.enable()
slow_function()
lpr.disable()
lpr.print_stats()
Вывод:
Line # Hits Time Per Hit % Time Line Contents
1 def slow_function():
2 1 1.0 1.0 0.0 result = 0
3 1000001 524123.0 0.5 99.9 for i in range(1000000):
4 1000000 522123.0 0.5 99.8 result += i ** 2
5 1 2.0 2.0 0.0 return result
5. memory_profiler для анализа памяти
# pip install memory-profiler
from memory_profiler import profile
@profile
def create_list():
big_list = [i ** 2 for i in range(1000000)]
return sum(big_list)
if __name__ == '__main__':
create_list()
Запуск:
python -m memory_profiler script.py
Вывод:
Line # Mem usage Increment Line Contents
2 40.3 MiB 0.0 MiB @profile
3 40.3 MiB 0.0 MiB def create_list():
4 70.5 MiB 30.2 MiB big_list = [i**2 for i in range(1000000)]
5 70.5 MiB 0.0 MiB return sum(big_list)
6. Практические примеры: медленный vs быстрый код
Пример 1: Поиск в списке
import timeit
# МЕДЛЕННО: линейный поиск в списке каждый раз
data = list(range(10000))
code_slow = """
for item in [5000, 7500, 2500, 9999, 1000]:
if item in data:
pass
"""
# БЫСТРО: преобразуем в set
data_set = set(range(10000))
code_fast = """
for item in [5000, 7500, 2500, 9999, 1000]:
if item in data_set:
pass
"""
time_slow = timeit.timeit(code_slow, globals=globals(), number=10000)
time_fast = timeit.timeit(code_fast, globals=globals(), number=10000)
print(f"Медленно: {time_slow:.4f} сек")
print(f"Быстро: {time_fast:.4f} сек")
print(f"Ускорение: {time_slow / time_fast:.1f}x")
Пример 2: Конкатенация строк
# МЕДЛЕННО: +=
code_slow = """
result = ""
for i in range(1000):
result += str(i)
"""
# БЫСТРО: join
code_fast = """
result = "".join(str(i) for i in range(1000))
"""
time_slow = timeit.timeit(code_slow, number=1000)
time_fast = timeit.timeit(code_fast, number=1000)
print(f"Медленно (+=): {time_slow:.4f} сек")
print(f"Быстро (join): {time_fast:.4f} сек")
print(f"Ускорение: {time_slow / time_fast:.1f}x")
Пример 3: Работа с БД
# МЕДЛЕННО: N+1 запросов
users = db.users.find_all() # 1 запрос
for user in users:
posts = db.posts.find_by_user(user.id) # N запросов!
process(user, posts)
# БЫСТРО: JOIN или eager loading
users_with_posts = db.query("""
SELECT users.*, posts.*
FROM users
LEFT JOIN posts ON users.id = posts.user_id
""")
for user, posts in group_by_user(users_with_posts):
process(user, posts)
7. Встроенная диагностика в FastAPI
from fastapi import FastAPI
import time
import logging
app = FastAPI()
logger = logging.getLogger(__name__)
@app.middleware("http")
async def log_request_time(request, call_next):
start = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start
logger.info(f"{request.method} {request.url.path}: {process_time:.3f} сек")
response.headers["X-Process-Time"] = str(process_time)
return response
8. Чеклист для выявления проблем
Спросите себя:
-
Сложность алгоритма:
- Использую ли я O(n²) где можно O(n)?
- Есть ли вложенные циклы?
- Использую ли я неправильную структуру данных?
-
I/O операции:
- Выполняю ли я запросы в цикле (N+1)?
- Использую ли я синхронные вызовы где нужны асинхронные?
- Нет ли лишних сетевых запросов?
-
Память:
- Создаю ли я большие объекты в памяти?
- Есть ли утечки памяти?
- Правильно ли освобождаю ресурсы?
-
Конкурентность:
- Есть ли блокирующие операции?
- Использую ли я asyncio/threading где нужно?
Итог
Инструменты:
timeit— быстрые микробенчмаркиcProfile— найти самые медленные функцииline_profiler— анализ по строкам кодаmemory_profiler— поиск утечек памятиmiddlewareв FastAPI — профилирование запросов
Золотое правило: "Измеряй, не гадай!" Всегда профилируй перед оптимизацией.