> С чем именно приходилось бороться в производительности (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: Сбер

Стек: Python

> Пример ответа

Короткий ответ

Основные проблемы производительности в Python-бэкенде: CPU-bound задачи из-за GIL, неэффективные ORM-запросы (N+1, лишние выборки), блокирующий I/O, рост потребления памяти, медленная сериализация/десериализация, неоптимальные алгоритмы и структуры данных, а также проблемы с кэшированием и конкурентностью. На практике чаще всего приходится бороться с запросами к БД и неэффективным использованием циклов и коллекций.

Подробное объяснение

В Python производительность упирается в несколько фундаментальных ограничений:

  • GIL - не даёт использовать несколько ядер для CPU-bound кода в рамках одного процесса. Решения: multiprocessing, asyncio для I/O-bound, C-расширения, переписывание горячих участков на Cython/C.
  • ORM-запросы - самый частый источник проблем. N+1 запросов, выборка лишних колонок, отсутствие индексов, тяжёлые JOIN'ы, материализация больших наборов в память.
  • Блокирующий I/O - синхронные вызовы к внешним сервисам, БД, файловой системе. Решение: async/await, пулы потоков, connection pooling.
  • Сериализация - JSON, pickle, msgpack. На больших объёмах данных это становится узким местом. Решение: orjson, ujson, protobuf.
  • Память - утечки, большие объекты, неэффективные структуры данных. Решение: профилирование памяти, использование __slots__, генераторы вместо списков.
  • Алгоритмическая сложность - использование O(n²) там, где можно O(n log n), неверный выбор коллекций (list вместо set для проверки вхождения).
  • Кэширование - отсутствие кэша, неверная стратегия инвалидации, слишком маленький TTL, кэш-промахи.

На практике

Начинаю с профилирования: cProfile, py-spy, memory_profiler, tracemalloc. Затем смотрю на:

  1. Запросы к БД - включаю логирование медленных запросов, проверяю EXPLAIN, добавляю индексы, переписываю на агрегации в SQL вместо Python-циклов.
  2. Горячие функции - выношу в отдельные модули, оптимизирую алгоритмы, заменяю рекурсию на итерации.
  3. I/O - перевожу на asyncio, использую aiohttp, asyncpg, aioredis. Для синхронных библиотек - ThreadPoolExecutor.
  4. Сериализацию - заменяю стандартный json на orjson, для внутренних сервисов - protobuf или msgpack.
  5. Память - ищу утечки через tracemalloc, оптимизирую хранение данных, использую слабые ссылки.

Типичный пример: сервис, который обрабатывает 100k запросов в час, упирается в N+1 запросы ORM. Решение - select_related/prefetch_related, или переход на сырые SQL-запросы для сложных выборок.

Пример кода

PYTHON
# Плохо: N+1 запросов
def get_orders_with_items(user_id):
orders = Order.objects.filter(user_id=user_id)
result = []
for order in orders:
items = order.items.all() # запрос на каждый order
result.append((order, items))
return result
# Хорошо: один запрос с prefetch_related
def get_orders_with_items_optimized(user_id):
orders = Order.objects.filter(user_id=user_id).prefetch_related('items')
return [(order, order.items.all()) for order in orders]
PYTHON
# Плохо: сериализация стандартным json
import json
data = [{"id": i, "name": f"item_{i}"} for i in range(100000)]
serialized = json.dumps(data)
# Хорошо: orjson в 2-3 раза быстрее
import orjson
serialized = orjson.dumps(data)

Как отвечать на собеседовании

Структурируй ответ по категориям: CPU, I/O, память, БД. Назови конкретные инструменты профилирования, которые используешь. Приведи реальный кейс из практики: что было узким местом, как диагностировал, что сделал, какой прирост получил. Покажи понимание trade-off: например, asyncio не решает CPU-bound проблемы, а multiprocessing добавляет overhead на межпроцессное взаимодействие. Упомяни, что сначала всегда профилируешь, а не оптимизируешь вслепую.

Что проверяет интервьюер

  • Понимание фундаментальных ограничений Python (GIL, глобальная интерпретация).
  • Умение системно подходить к диагностике: профилирование, метрики, логирование.
  • Знание инструментов: cProfile, py-spy, memory_profiler, EXPLAIN, slow query log.
  • Практический опыт: конкретные решения, а не теоретические рассуждения.
  • Способность оценивать trade-off: скорость разработки vs производительность, сложность vs выгода.
  • Понимание, что оптимизация - это итеративный процесс с измерением результата.

Типичные ошибки

  • Начинать оптимизировать без профилирования - "оптимизация вслепую".
  • Сводить всё к GIL и сразу предлагать multiprocessing, не проверив, что проблема действительно CPU-bound.
  • Игнорировать БД как источник проблем - большинство узких мест именно там.
  • Предлагать asyncio для CPU-bound задач.
  • Не учитывать память: оптимизировать по времени, но забывать про потребление RAM.
  • Использовать преждевременную оптимизацию: усложнять код ради гипотетического прироста.
  • Не измерять результат после изменений - без бенчмарков нельзя подтвердить улучшение.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью