Ephemeral vs Ray: загрузка моделей и утилизация GPU в 2026 | AiManual
AiManual Logo Ai / Manual.
22 Янв 2026 Гайд

Ephemeral vs Ray: Сравнение подходов к загрузке моделей и утилизации GPU в продакшене

Глубокий разбор двух архитектур для AI-инференса: эфемерные модели против оркестрации Ray. Как поднять утилизацию GPU с 15% до 85% и убить холодные старты.

GPU простаивает, а инференс тормозит? Знакомо

Вы развернули модель в продакшене. Запросы приходят неравномерно. То шквал, то тишина. GPU то грустит на 5% загрузки, то захлебывается на 95%. Холодные старты по 30 секунд. И счет за облако растет.

Перед вами классическая дилемма 2026 года: использовать тяжелую оркестрацию вроде Anyscale Ray или легковесный подход с эфемерными моделями (как в репозитории InferX). Первый дает контроль, второй — скорость. Разберем оба на костях.

Актуальность на 22.01.2026: Ray 2.10+ уже поддерживает динамическое масштабирование GPU-воркеров с прерываемыми инстансами. InferX и аналоги эволюционировали в сторону загрузки не только из RAM, но и из сверхбыстрых NVMe-массивов (например, Intel Optane Persistent Memory 300 Series). Модели вроде DeepSeek-R1 671B или Claude-4.5 весят сотни гигабайт, делая вопрос утилизации критичным.

Ray: армейская дисциплина для ваших GPU

Представьте диспетчера на складе. Грузчики (воркеры) ждут заданий. Подъехала фура — диспетчер будит спящих, распределяет нагрузку. Это Ray. Его цель — максимальная утилизация ресурсов через пулы и очереди.

Как это работает в коде (Ray 2.10.1)

# Декоратор превращает ваш класс в сервис, живущий в кластере Ray
from ray import serve
from transformers import AutoModelForCausalLM
import torch

@serve.deployment(
    ray_actor_options={\"num_gpus\": 1},  # Каждый реплика получает свою GPU
    autoscaling_config={
        \"min_replicas\": 1,
        \"max_replicas\": 10,  # Масштабируем под нагрузку
        \"target_num_ongoing_requests_per_replica\": 2  # Магия балансировки
    }
)
class LLMDeployment:
    def __init__(self, model_id: str):
        # Модель загружается в память ВО ВРЕМЯ ИНИЦИАЛИЗАЦИИ воркера
        self.model = AutoModelForCausalLM.from_pretrained(
            model_id,
            torch_dtype=torch.float16,
            device_map=\"auto\"
        )
        self.tokenizer = AutoTokenizer.from_pretrained(model_id)
    
    async def __call__(self, request):
        # Запрос попадает в свободную реплику
        prompt = await request.json()
        inputs = self.tokenizer(prompt, return_tensors=\"pt\").to(\"cuda\")
        with torch.no_grad():
            outputs = self.model.generate(**inputs, max_new_tokens=100)
        return self.tokenizer.decode(outputs[0])

# Запускаем сервис
app = LLMDeployment.bind(\"Qwen/Qwen2.5-32B-Instruct\")

Плюсы подхода:

  • Автомасштабирование: Ray сам добавляет или убирает реплики на основе метрик (RPS, задержка, использование GPU).
  • Высокая доступность: Если воркер падает, Ray перезапускает его и перенаправляет трафик.
  • Сложные сценарии: Можно строить графы из моделей (цепочки вызовов), что актуально для RAG-систем.

Минусы, которые бесят:

  • Накладные расходы: Каждый воркер — отдельный процесс Python со своей загруженной моделью. 10 реплик = 10 копий модели в VRAM. (Да, Ray умеет шарить память через CUDA MPS, но это танцы с бубном).
  • Холодный старт реплики: Добавление нового воркера под нагрузку — это 30-120 секунд загрузки модели с диска или из object storage. Запросы в это время ждут или падают.
  • Сложность отладки: Distributed tracing в Ray — это отдельный ад. Когда запрос теряется где-то между пятью воркерами, хочется плакать.
💡
Ray идеален для стабильной, предсказуемой нагрузки, когда у вас есть бюджет на избыточные ресурсы. Если у вас пики в 10 раз превышают базовую нагрузку — готовьте кошелек. Об этом же пишут в статье \"Дешёвый AI-инференс: как они это делают\".

Эфемерные модели: снайперский выстрел из RAM

А теперь представьте, что грузчиков нет. Есть робот-манипулятор, который моментально хватает нужный груз с полки и отгружает. Полка — это общая RAM или сверхбыстрый NVMe. Робот — легковесный процесс, который за миллисекунды маппит модель в память GPU и выполняет инференс. После работы — выгружает, освобождая VRAM.

Это подход disaggregated inference (разъединенный инференс), который популяризировали InferX и аналоги в 2024-2025 годах. На 22.01.2026 он эволюционировал в стандарт для сервисов с резко переменной нагрузкой.

Архитектура за 60 секунд

  1. Модель (например, Llama-3.3 70B в формате GGUF) лежит в общей памяти всех узлов (RAM или PMEM).
  2. Менеджер запросов (часто на Go или Rust) принимает HTTP-запрос.
  3. Он запускает ephemeral-контейнер (или процесс), который маппит файл модели прямо в адресное пространство.
  4. Контейнер загружает веса в GPU через прямой доступ к памяти (DMA), выполняет инференс и умирает.
  5. VRAM немедленно освобождается для следующей задачи.
# Упрощенная схема работы InferX-подобного решения
import subprocess
import json
from typing import Optional

class EphemeralInference:
    def __init__(self, model_path: str, gpu_id: int = 0):
        self.model_path = model_path  # Модель уже в RAM (/dev/shm/model.gguf)
        self.gpu_id = gpu_id
    
    def infer(self, prompt: str, max_tokens: int = 100) -> Optional[str]:
        # Запускаем одноразовый процесс для инференса
        # В реальности здесь будет нативный бинарник на C++ с llama.cpp
        cmd = [
            \"./inferx_worker\",
            f\"--model={self.model_path}\",
            f\"--gpu={self.gpu_id}\",
            f\"--prompt={prompt}\",
            f\"--max-tokens={max_tokens}\"
        ]
        
        try:
            # Критически важно: timeout чуть больше expected inference time
            result = subprocess.run(cmd, capture_output=True, timeout=30.0)
            if result.returncode == 0:
                return json.loads(result.stdout)[\"text\"]
            else:
                # Процесс умер, но это не страшно — запустим новый
                logger.error(f\"Inference failed: {result.stderr}\")
                return None
        except subprocess.TimeoutExpired:
            # Убиваем зависший процесс, GPU освобождается
            process.kill()
            return None

# Использование
infer = EphemeralInference(\"/dev/shm/llama-3.3-70b.Q4_K_M.gguf\")
response = infer.infer(\"Explain quantum computing\")
print(response)

Почему это летает:

  • Нулевые cold starts для модели: Файл уже в RAM, маппинг происходит за миллисекунды. Нет загрузки с диска.
  • Утилизация GPU под 90%: Пока одна модель думает, на ту же GPU можно поставить другую задачу (например, инференс меньшей модели или эмбеддинг).
  • Нет оверхеда на оркестрацию: Нет центрального планировщика. Менеджер запросов просто кидает задачи на свободные GPU.

Подводные камни:

  • Сложность управления состоянием: Сессии, streaming responses (SSE) — все это нужно реализовывать поверх одноразовых процессов. Не для слабонервных.
  • Риск утечек памяти: Если процесс после инференса не полностью чистит GPU memory, через час работы у вас 48 ГБ VRAM будут заняты \"призрачными\" тензорами. Требует перезагрузки драйвера.
  • Ограниченная экосистема: Поддерживаются в основном модели в GGUF/GGML форматах. Попробуйте так запустить PyTorch-модель со сложным препроцессингом — получите головную боль.
💡
Эфемерный подход — это как Formula 1: максимальная производительность при минимальном весе, но механик нужен после каждого заезда. Если ваша нагрузка — это короткие, но интенсивные всплески (чат-бот в телеграме, который просыпается раз в час), это ваш выбор. Для долгих диалогов — лучше Ray с сессиями.

Лоб в лоб: сравнительная таблица

КритерийRay (Anyscale)Ephemeral (InferX-подобный)
Утилизация GPU при неравномерной нагрузке40-60% (реплики простаивают)75-90% (GPU занят постоянно)
Cold start (модель 70B)30-120 секунд (загрузка новой реплики)100-500 мс (маппинг из RAM)
Сложность эксплуатацииВысокая (кластер, мониторинг, autoscaling)Средняя (нужен свой менеджер процессов)
Стоимость инфраструктурыВысокая (постоянные реплики + overhead)Низкая (только под нагрузку)
Поддержка сложных пайплайновОтличная (графы, DAG)Ограниченная (последовательные вызовы)
Лучший сценарийКорпоративный ML-платформа, стабильный RPSСервисы с редкими, но тяжелыми пиками (ночные batch-обработки)

Как выбрать? Пошаговый план для инженера

1Проанализируйте паттерн нагрузки

Вытащите метрики за последний месяц. Если у вас ровный график запросов (как у API для внутренних сервисов) — Ray. Если видны четкие пики в 100 раз выше базового уровня (как у публичного чат-бота, который упомянули в СМИ) — эфемерные модели. Инструменты: Prometheus, Grafana, или просто лог nginx.

2Посчитайте стоимость простоя GPU

A100 80GB на гиперскалере в 2026 стоит около $3.5/час. Если он простаивает 70% времени, вы платите $2500 в месяц за воздух. Эфемерный подход позволит запускать на этом же GPU другие задачи (например, инференс маленьких моделей для классификации) в периоды простоя. Ray тоже умеет mixed workloads, но сложнее. Подробнее в статье \"Как выбрать самый дешёвый GPU-провайдер\".

3Определите требования к latency

Если 99-й перцентиль задержки должен быть ниже 200 мс (финтех, голосовые ассистенты), холодные старты Ray вас убьют. Эфемерные модели дают предсказуемую latency, но только если модель уже в RAM. Если нужно загружать с диска — все преимущества теряются. Решение: держать горячий пул из 1-2 предзагруженных моделей в эфемерном менеджере.

4Прототипируйте на 20% трафика

Не переписывайте все сразу. Возьмите один эндпоинт (например, генерация ответов в поддержку) и запустите его на эфемерной архитектуре. Сравните метрики: утилизация GPU, latency, стоимость. Для Ray используйте локальный кластер на одной машине с несколькими GPU. Инструкции есть в их документации.

Предупреждение: Не пытайтесь скрестить эти подходы на одном GPU без изоляции. Если Ray-воркер и эфемерный процесс попробуют поделить одну видеопамять, вы получите CUDA out of memory даже на 80 ГБ. Изолируйте через MIG (Multi-Instance GPU) или просто физически разные карты.

Ошибки, которые сломают вам продакшен

  • Эфемерные модели без контроля памяти: Запускаете процессы, забываете их убивать. Через час все GPU memory занято orphaned процессами. Решение: использовать cgroups или container runtime (например, runc) с гарантированным cleanup после таймаута.
  • Ray с неправильным autoscaling config: Поставили target_num_ongoing_requests_per_replica: 1. При каждом запросе Ray пытается добавить реплику. Получаете флаттер реплик и перманентные cold starts. Выставляйте значение, равное 50-70% от пропускной способности одной реплики.
  • Хранение моделей на сетевом диске (NFS) для эфемерного подхода: Тогда ваш \"быстрый\" cold start превратится в 10-секундную загрузку по сети. Модели должны быть в RAM или локальном NVMe. Если RAM не хватает — посмотрите в сторону гибридных GPU-связок.
  • Игнорирование квотирования (quotas): В облаках есть лимиты на запуск инстансов с GPU. Если ваш эфемерный менеджер попытается запустить 100 процессов одновременно, API облака откажет. Нужна очередь запросов с backpressure.

Вопросы, которые вы хотели задать, но боялись

Можно ли использовать эфемерные модели для MoE (Mixture of Experts) архитектур?

Теоретически — да. Практически — ад. MoE-модели (как Mixtral 8x22B) требуют динамической загрузки разных экспертов в память. Эфемерный процесс должен маппить все файлы экспертов сразу, что съедает всю память. Ray здесь выигрывает, так как может держать экспертов в разных репликах и маршрутизировать запросы. Но если очень хочется, посмотрите на техники запуска MoE на ограниченной VRAM.

Что насчет новых GPU с 128 ГБ HBM4? Изменит ли это баланс?

К 2026 году HBM4 действительно обещает до 128 ГБ на чип. Это сделает Ray-подход более привлекательным: можно держать несколько больших моделей в памяти одной карты без постоянной перезагрузки. Но закон Паркинсона (\"данные заполняют все доступное пространство\") никто не отменял. Модели тоже вырастут. Так что борьба за утилизацию останется.

Какой подход используют крупные AI-провайдеры (OpenAI, Anthropic) в 2026?

По слухам (и вакансиям) — гибридный. Базовый слой — Ray или Kubernetes с custom scheduler для оркестрации. Но для обработки пиковых нагрузок используют disaggregated inference pools, где модели загружены в огромную общую память (1-2 ТБ RAM на узел). Запросы маппятся на свободные GPU как в эфемерном подходе. Это дорого, но для масштаба в миллионы RPS — оправдано.

Финальный совет: не выбирайте навсегда

Архитектура инференса — не религия. Начните с простого: если у вас 1-2 модели и нагрузка растет медленно, берите Ray — он даст стабильность и экосистему. Когда увидите, что 70% времени GPU простаивает, добавьте эфемерный пул для batch-обработок или второстепенных моделей.

Самый страшный грех — это держать GPU на 5% загрузки потому что \"так исторически сложилось\". Каждый процент утилизации — это реальные деньги. В 2026 году счета за облако кусаются так, что даже CFO замечает. Удачи.