
Как я ускорил dependency injection в Python в 130 раз: от рефлексии до компиляции графа
VShulcz 15 минут назад Как я ускорил dependency injection в Python в 130 раз: от рефлексии до компиляции графа Средний 4 мин 508 Python * Высоконагруженные системы * Open source * Программирование * Кейс Про DI в Python...
Anthropic — What company has the best second artificial intelligence model at the end of June?
Значимый прорыв формирует отрасль ИИ: VShulcz 15 минут назад Как я ускорил dependency injection в Python в 130 раз: от рефлексии до компиляции графа Средний 4 мин 508 Python * Высоконагруженные системы * Open source * Программирование * Кейс Про DI в Python вечно всплывает один и тот же спор: контейнер — это лишний оверхед, протащи зависимость в конструктор руками и не выдумывай. Звучит логично, я и сам так долго считал. Но логично — не число, поэтому в какой-то момент я сел и замерил: во что на самом деле обходится контейнер, когда резолвишь граф по кругу, и можно ли вообще догнать ручную сборку, не сломав при этом семантику.
Спойлер: подойти вплотную можно. Но интереснее тут не финальная цифра, а дорога к ней — почти каждый шаг пригодится и за пределами DI. Как ловить микрооверхед, который не виден в одном вызове.
Технические детали
Как не бояться выкидывать код, который и так никогда не выполняется. И как не дать exec-кодогенерации молча сломать прод. Как резолв DI ускорился с 52.
40 мкс/опСтендГраф маленький, но жизненный для бэкенда: сверху синглтоны — конфиг и клиент, ниже транзиентные репозиторий, отправщик писем и аудит, и use-case RegisterUser, который тянет все три. Бенчмарк гоняет повторный резолв этого графа по кругу; рядом, как нижняя граница, — те же объекты, собранные руками. Машина одна и та же на всех замерах.
Числа синтетические и завязаны на форму графа. Отсчёт оказался отрезвляющим: руками — 0. 27 мкс на операцию, наивный контейнер — 52.
Отраслевые последствия
Почти в двести раз медленнее. Чтобы было ясно, что цифра взята не с потолка: punq, обычный рефлексивный контейнер, на том же графе даёт около 57 мкс. Так и выходит, если разбирать конструкторы на каждый вызов.
Откуда берутся 53 микросекундыНаивный резолвер на каждый вызов лезет в конструктор: берёт inspect. signature, дёргает get_type_hints, по аннотациям рекурсивно достаёт зависимости и создаёт объект. Беда в том, что get_type_hints и разбор сигнатуры — дорогие: там вычисление аннотаций, обход MRO, аллокации.
Миллион раз нподряд — десятки+ микросекунд. Напрашивается очевидное: разобрать граф один раз. При регистрации (или на первом резолве) читаем конструктор и складываем план: какие зависимости, в каком порядке, с каким временем жизни.
Событие, по словам экспертов, усилит конкуренцию в сфере ИИ.





