Как проверить производительность приложения?
Что проверяют
Умеете ли вы найти, где именно тормозит Python-приложение, а не оптимизировать наугад. Интервьюер ждёт разделения на замеры функций, профилирование и нагрузку на весь сервис. Следом часто спрашивают про GIL и про то, почему потоки не ускорили расчёт.
Как отвечать
Сначала определите, что медленно: время ответа сервиса, память или конкретная функция. От этого зависит инструмент.
- микробенчмарки:
python -m timeitиpytest-benchmark, который сохраняет результаты и сравнивает прогоны; - детерминированный профилировщик:
cProfileплюсpstatsили snakeviz. Показывает число вызовов и накопленное время, но сам добавляет заметный оверхед; - сэмплирующий профилировщик:
py-spyподключается к работающему процессу по PID без перезапуска (py-spy top --pid,py-spy record -o profile.svg --pid) и почти не замедляет его, поэтому годится для прода; - построчно:
line_profilerдля уже найденной горячей функции; - память:
tracemallocиз стандартной библиотеки,memrayили Scalene; - сервис целиком: Locust или k6 для нагрузки, метрики в Prometheus и трассировки через OpenTelemetry, чтобы увидеть, что время уходит в базу, а не в код.
Дальше свяжите находки с природой задачи. CPU-bound коду в обычной сборке CPython потоки не помогут из-за GIL, нужны multiprocessing, векторизация через NumPy или нативный код. В асинхронном сервисе частая беда в блокирующем вызове внутри event loop: режим отладки asyncio (PYTHONASYNCIODEBUG=1) пишет в лог колбэки дольше 100 мс.
Где спотыкаются
Проверяют результат функции и называют это тестом производительности. assert f() == 333383335000 проверяет корректность, а не скорость.
Меряют один запуск через time.time(). Для замеров есть time.perf_counter() и много повторов, иначе шум больше разницы.
Доверяют абсолютным цифрам cProfile на мелких функциях: оверхед профилировщика искажает именно их, и узкое место оказывается не там.