Адаптивная маршрутизация LLM-запросов по GPU
В LLM-инференсе есть несколько GPU-воркеров. Почему наивная round-robin маршрутизация может быть неэффективной, и как спроектировать адаптивный слой маршрутизации с учетом загрузки GPU, KV cache и длины запроса?
Ответить самому
Сначала сформулируйте ответ как на собеседовании, затем откройте разбор и оцените себя.
Короткий ответ
Round-robin не учитывает разную длину запросов, prefill/decode нагрузку, память под KV cache и возможность переиспользовать уже прогретый cache. Лучше поставить scheduler перед GPU workers: он смотрит на очередь, свободную KV-cache memory, compute utilization, prefix/session affinity и route-ит запрос туда, где expected latency минимальна.
Полный разбор
Naive round-robin хорош только когда запросы примерно одинаковые и workers находятся в похожем состоянии. В LLM serving это редко так: один запрос может иметь длинный prompt и тяжелый prefill, другой - короткий decode; один worker уже хранит полезный KV cache для похожего prefix/session, другой близок к memory pressure. Round-robin в такой ситуации легко создает tail latency, неравномерную загрузку и лишние cache misses.
Практичная архитектура: перед model workers ставится routing/scheduling layer. Он собирает runtime-сигналы по каждому worker: queue length, active sequences, free KV-cache memory, prefill/decode utilization, expected tokens, текущий memory headroom, OOM/backpressure flags и cache locality. Для нового запроса scheduler сначала проверяет, есть ли prefix/session/model affinity, где можно переиспользовать KV cache. Если да, он предпочитает этот worker при условии, что там достаточно headroom. Если cache affinity нет, выбирает worker по predicted completion time или weighted score: меньше очередь, больше свободной памяти, ниже текущая decode нагрузка.
Важно не превратить это в хрупкую эвристику. Нужны guardrails: admission control, fallback к простому load balancing, eviction policy для KV cache, TTL для cache entries, fairness между пользователями, лимиты на большие prompts и monitoring. Для batching scheduler должен учитывать, какие запросы можно эффективно объединить: похожий decode phase, совместимые sequence lengths и одинаковые model/runtime параметры.
Оценивать нужно не только средний throughput. Основные метрики: time to first token, tokens/sec, p95/p99 latency, GPU utilization, KV-cache hit rate, OOM rate, queue wait time и cost per generated token. Хорошая реализация должна улучшать tail latency и effective throughput без деградации стабильности.
Теория
LLM inference состоит из prefill и decode фаз с разными bottleneck. Prefill чаще compute-heavy, decode часто memory-bandwidth/KV-cache sensitive. Поэтому routing должен учитывать не только количество запросов, но и фазу, длину контекста, cache locality и memory headroom.
Типичные ошибки
- Предложить обычный round-robin или least-connections без учета длины контекста и decode phase.
- Игнорировать KV cache как основной источник memory pressure и возможного reuse.
- Оптимизировать только throughput и не смотреть p95/p99 latency, OOM и fairness.
Как отвечать на собеседовании
- Сначала объясни, почему запросы в LLM serving неодинаковые: prompt length, generation length, prefill/decode.
- Назови конкретные runtime-сигналы scheduler-а: queue length, KV-cache memory, GPU utilization, cache affinity.
- Заверши метриками: TTFT, p95 latency, tokens/sec, cache hit rate, OOM rate.