tgoop.com/pyproglib/7017
Last Update:
👉 Мифы и сказки вокруг производительности Python
Многие считают, что Python достаточно быстр, ведь это язык-«клей», где для тяжёлых вычислений используют GPU или вызывают C/C++/Rust-библиотеки. Но на самом деле, для множества задач Python всё же медленен.
Это действительно часто помогает — оптимизировать 20% кода, где происходит 80% работы (принцип Парето). Однако Amdahl's law говорит, что ускорение одной части программы в итоге упирается в остальной код. После ускорения «горячих» мест, всё остальное начинает доминировать по времени исполнения.
Интерпретация даёт некоторое замедление, но гораздо больше времени уходит на выполнение динамической семантики Python: поиск типов, вызовы методов вроде __getattribute__(), упаковку и распаковку значений, выделение памяти и т.д. Это не зависит от того, интерпретируется код или компилируется.
Python набирает популярность с аннотациями типов, но статическая типизация не проверяется во время выполнения. Например:
def add(x: int, y: int) -> int:
return x + y
print(add('hello ', 'world')) # type: ignore
Здесь добавление строк работает, но не соответствует типам. Значит, оптимизации на основе типов невозможны в чистом Python.
JIT-компиляторы (например, в PyPy) действительно могут ускорить Python, но они усложняют предсказуемость производительности и требуют понимания, как именно JIT оптимизирует код. Иногда оптимизации «ломаются» при изменении программы, даже если код кажется простым.
Рассмотрим простой алгоритм:
def algo(points: list[tuple[float, float]]):
res = 0
for x, y in points:
res += x**2 * y + 10
return res
Если вынести вычисление в отдельную функцию:
def fn(x, y):
return x**2 * y + 10
def algo(points: list[tuple[float, float]]):
res = 0
for x, y in points:
res += fn(x, y)
return res
То производительность падает из-за накладных расходов на вызовы функций. Если вместо кортежей использовать
@dataclass:from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def fn(p: Point):
return p.x**2 * p.y + 10
def algo(points: list[Point]):
res = 0
for p in points:
res += fn(p)
return res
Скорость падает ещё больше, потому что добавляется обращение к атрибутам объектов и накладные расходы, связанные с объектной моделью.
Основная проблема Python — не вычисления, а память. Современные процессоры быстро выполняют операции, но доступ к памяти (особенно к ОЗУ) намного медленнее.
Python-программы часто имеют плохую кэш-локалити из-за разброса объектов в памяти:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = [Person('Alice', 16), Person('Bob', 21)]
В памяти объекты
Person и их атрибуты могут быть разбросаны, что приводит к множеству переходов по указателям и к частым кеш-промахам.Это проблема, которую невозможно решить только компиляцией или JIT, не изменяя семантику языка.
Библиотека питониста #буст

