Производительность и профилирование
Сначала измерь, оптимизируй узкое место, затем измерь снова
timeit и cProfile, рассуждаете о том, как растёт стоимость с размером входных данных (big-O), и только затем меняете именно ту часть, которая важна — обычно выбором лучшего алгоритма или структуры данных, кэшированием повторяющейся работы или переносом плотных циклов в векторизованные операции. Остальной код оставляйте простым и понятным.
- правильно замерить небольшой фрагмент с помощью
timeitи обойти его типичные ловушки, - профилировать целую программу с
cProfileи читать отчётpstats, - читать рост big-O и связывать его с реальным временем,
- заменить проверку принадлежности за
O(n)наO(1)черезset/dict, - кэшировать повторную работу с
functools.lru_cache, - понимать, когда векторизовать горячий цикл вместо ручной записи.
1. Измеряй, а не гадай
Первое правило работы над производительностью: интуиция программиста о том, "что медленно", обычно ошибочна. Интерпретатор, операционная система, кэши памяти и внутренности библиотек взаимодействуют так, что предсказать это по чтению кода трудно. Поэтому профессиональный цикл таков: измерить -> найти узкое место -> изменить одну вещь -> измерить снова.
Связанное правило: оптимизируй только то, что важно. Функция, выполняемая один раз при запуске, не заслуживает того внимания, какого заслуживает функция, вызываемая десять миллионов раз внутри цикла. Знаменитая фраза Дональда Кнута — "преждевременная оптимизация — корень всех зол" — на самом деле о том, чтобы не тратить усилия там, где они не окупаются. Понятный код — это стандарт; вы жертвуете ясностью ради скорости только на доказанном узком месте.
timeit) отвечает: "какой из этих двух маленьких фрагментов быстрее?". Профилировщик (cProfile) отвечает: "во всей этой программе, куда реально уходит время?". Выбирайте подходящий.