Ускоряем стартап Python-приложения: коротко

от автора

Привет, Хабр!

Сегодня рассмотрим как ускорить стартап Python-приложений. Холодный старт — это прямые потери: в деньгах, в SEO, в отклике для пользователя. Serverless считает миллисекунды, edge-инфраструктура не ждёт, а тяжёлые импорты и неподготовленное окружение легко съедают полсекунды. Будем это чинить.

Минимизируем импорты на старте

Чтобы приложение взлетало быстро, первым делом убираем всё лишнее из зоны старта.

Сначала замеряем импорты

python -X importtime -m myapp 2> import.log python -m snakeviz import.log

В проектах бывает типичный разнос:

Модуль

Импорт за, мс

pandas

320

numpy

180

scipy

540

Делаем импорты локальными там, где можно

def enrich(df_like):     import pandas as pd  # noqa     df = pd.DataFrame(df_like)     df["ts"] = pd.Timestamp.utcnow()     return df.to_dict(orient="records")

Импорт pandas будет происходить только, если реально вызовется эта функция.

Если глобальный импорт всё-таки нужен — используем LazyLoader

import importlib, sys  _spec   = importlib.util.find_spec("tensorflow") _loader = importlib.util.LazyLoader(_spec.loader) module  = importlib.util.module_from_spec(_spec) _loader.exec_module(module) sys.modules["tensorflow"] = module

Заводим лёгкий entrypoint

# main.py def run():     from myapp.server import app     app.run()  if __name__ == "__main__":     run()

Файлик main.py стартует за миллисекунды, потому что не тянет за собой кучу импортов сразу.

Теперь логичный шаг — прогреть окружение, чтобы сам контейнер быстро загружался.

Подготавливаем окружение заранее

Убираем тормоза уже на уровне контейнера и зависимостей.

Используем provisioned concurrency

На AWS Lambda можно держать контейнеры в прогретом состоянии. Платишь чуть больше, зато старта 0 мс.

Строим slim-образы

FROM python:3.12-slim-bookworm as builder RUN pip install poetry WORKDIR /src COPY pyproject.toml poetry.lock ./ RUN poetry install --no-dev  FROM python:3.12-slim-bookworm COPY --from=builder /usr/local /usr/local COPY ./app /app CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "myapp.api:app"]

Slim-образ размером ≈ 120 МБ против обычных 800 МБ — скачивание слоёв и раскрутка сети сокращаются в разы.

Убираем pip install из старта

poetry export -f requirements.txt > requirements.txt pip wheel -r requirements.txt -w vendor pip install --no-index --find-links=vendor -r requirements.txt 

Или ещё лучше — собираем один огромный fat wheel.

Теперь ускорим сами тяжёлые места в коде.

Компиляция hotspot-ов + асинхронная подготовка данных

Сами по себе импорты уже оптимальны, окружение лёгкое — значит, работаем с тяжёлыми вычислениями и кэшами.

Выносим тяжёлые функции в Cython

# fastmath.pyx cimport cython  @cython.cfunc def norm(double[:] v):     cdef double s = 0     for i in range(v.shape[0]):         s += v[i] * v[i]     return s ** 0.5

Компилируем:

python setup.py build_ext --inplace

Импорт .so модуля занимает < 1 мс.

Асинхронно загружаем тяжёлые данные через PEP 684

import interpreters, asyncio, json, gzip  CACHE = {}  def load_big_cache(path):     with gzip.open(path, "rb") as f:         CACHE.update(json.load(f))  async def bootstrap():     interp = interpreters.create()     interpreters.run_async(interp, load_big_cache, "/data/cache.json.gz")     from myapp import app     return app  app = asyncio.run(bootstrap())

Пока кэш греется в фоне, приложение уже обрабатывает первые запросы.

Финальный бенчмаркинг

Конфиг

p95 cold, мс

p95 warm, мс

Обычный образ + глобальные импорты

920

24

+ Slim + Lazy imports

620

23

+ Fat wheel

410

22

+ Cython + PEP 684

310

22

Итог

Минимизируем импорты, используем slim-образы с готовыми зависимостями, компилируем узкие места и подгружаем тяжёлые данные асинхронно — результат: cold start ускоряется в 2–3 раза без ущерба коду и стабильности.


В завершение хочу порекомендовать бесплатные вебинары курса Python Developer. Professional, на которые могут зарегистрироваться все желающие:


ссылка на оригинал статьи https://habr.com/ru/articles/905650/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *