С чего начинать оптимизацию

Python обладает всеми достоинствами, кроме скорости. Интерпретация, динамическая типизация, объектная модель — всё, что делает язык удобным, обходится дорого. Наивный цикл на Python выполняется в десятки и сотни раз медленнее того же кода на C. Для скрипта, запускаемого раз в день, это несущественно, а для обработки терабайта данных с детектора или моделирования миллиона частиц разница составляет часы против недель.

Медленный код на Python почти всегда можно ускорить, не переписывая его целиком на C++. В настоящем разделе последовательно рассматриваются следующие темы:

  • глава «Причины низкой скорости Python» объясняет, откуда берётся разница с C, и вводит инструменты измерения времени: cProfile, line_profiler, py-spy, timeit;
  • глава «Оптимизация средствами самого Python» собирает приёмы, не требующие ничего, кроме самого языка: выбор структуры данных, встроенные функции, экономия памяти;
  • глава «Скорость выполнения программ» последовательно ускоряет один и тот же расчёт: сначала с помощью Numba, затем Cython и в завершение NumPy;
  • глава «Многопоточность и GIL» объясняет, почему потоки в Python не ускоряют вычисления, что представляет собой глобальная блокировка интерпретатора и в каких случаях помогают порождённые процессы;
  • глава «Асинхронность» рассматривает цикл событий и asyncio, опираясь на корутины из главы «Итераторы, генераторы и корутины», и показывает, как ожидать тысячи одновременно запущенных операций ввода-вывода;
  • глава «CUDA и вычисления на GPU» показывает, когда и как перенести расчёт на видеокарту с помощью CuPy и Numba.

Оптимизацию не следует проводить вслепую. Интуиция почти всегда ошибается относительно того, где программа проводит время; достоверно это показывает только профилировщик.