Батчинг и несколько пользователей
По одному запросу за раз начинается каждая десктопная сессия — и это сжигает GPU впустую. Вся игра обслуживания — батчинг: заставить один проход по весам обслуживать много запросов сразу. Эта страница покрывает, почему это работает, что такое непрерывный батчинг и paged attention на самом деле, когда нужен настоящий движок обслуживания и как это размерить.
Почему последовательное обслуживание сжигает GPU
Вспомните формулу скорости: каждый декодированный токен читает практически все активные веса один раз (скорость). Обслуживая один поток, GPU делает всё это чтение ради одного токена одного пользователя. Теперь соберите восемь потоков в один прямой проход: веса читаются один раз и используются для восьми последовательностей — лишняя арифметика почти бесплатна, потому что декодирование ограничено пропускной способностью, а не вычислениями. Скорость на поток едва падает, а суммарная пропускная способность растёт почти линейно, пока не насытится пропускная способность или KV-память. Этот единственный факт — вся экономика локального обслуживания.
Статический против непрерывного батчинга
Наивная версия — статический батчинг — группирует запросы, пришедшие вместе, и гоняет батч, пока не закончит каждый участник. Работает, но плохо:
| Статический батчинг | Непрерывный батчинг | |
|---|---|---|
| Формирование батча | Один раз, когда группа соберётся | Переоценивается на каждом шаге декодирования |
| Вход на лету | Нет — ждёт следующего батча | Да, слоты заполняются по мере прихода запросов |
| Короткий запрос в батче длинных | Блокируется до конца самого длинного | Завершается и сразу освобождает слот |
| Хвостовая задержка | Плохая (блокировка головы очереди) | Хорошая |
Непрерывный (поитерационный) батчинг — дефолт в vLLM и SGLang — причина, почему эти движки держатся под реальным трафиком, пока наивные серверы рушатся.
Paged attention: лечение фрагментации KV
Каждый активный запрос нуждается в памяти KV-кэша (см. KV-кэш). Старый подход резервировал максимальный контекст каждого запроса авансом — так что чат на 2k токенов мог держать зарезервированный блок на 32k, запертый и непригодный для других. Большая часть пула KV простаивала. Paged attention хранит KV в блоках фиксированного размера, выделяемых по требованию, как ОС paging-ует память: отходы падают с большинства (авторы vLLM сообщали 60–80% в своей статье) до нескольких процентов, влезает в разы больше одновременных последовательностей, и пропускная способность следует. Та же идея, теперь стандарт в обслуживающих движках.
Цена для вас: ничего видимого пользователю — это изменение аллокатора; вы просто видите выше одновременную ёмкость на той же GPU.
Когда нужен настоящий движок обслуживания
| Нагрузка | Движок |
|---|---|
| Вы плюс хозяйство, несколько последовательных чатов | Ollama / LM Studio — уже нормально |
| Несколько разработчиков неторопливо параллельно | llama-server с параллельными слотами или параллельная среда Ollama |
| Продуктовый трафик, командный чат, реальные пользователи | vLLM или SGLang |
| Флоты агентов с длинными общими системными промптами | SGLang (кэш префиксов) или vLLM |
Игровой угол этого форума — канонический пример последней строки: матчи шахмат и джанги гоняют несколько агентов, каждый запрашивает ход примерно раз в секунду — маленькие промпты, много потоков, общие системные промпты. Это ровно та нагрузка, которой нужен обслуживающий движок с батчингом, а не ollama run в цикле (см. агенты).
Пропускная способность против задержки
Каждая ручка меняет одно на другое: большие батчи поднимают суммарные tok/s, слегка опуская скорость на поток, а очередь поднимает TTFT под нагрузкой. Кодинг-ассистент для одного человека хочет параллелизм, ограниченный 1–2, чтобы поток человека никогда не стоял в очереди. Чат-сервис для тридцати человек хочет набитых батчей. Решите, кем вы являетесь, прежде чем тюнить (см. обслуживание насчёт TTFT против декодирования как отдельных измерений).
Ближайшее к бесплатному обеду — кэширование префиксов (SGLang): повторные длинные промпты превращаются в почти нулевой префилл, а это большинство агентных нагрузок.
Практичные ручки
# llama.cpp: параллельные слоты декодирования (контекст делится между слотами)
llama-server -m model.gguf -c 32768 -np 4
# Ollama: параллельные запросы на модель (перезапустите сервис после установки)
export OLLAMA_NUM_PARALLEL=4
# vLLM: две, которые важны
vllm serve model --max-num-seqs 32 --gpu-memory-utilization 0.90
Очереди образуются выше всех этих: когда каждый слот занят, новые запросы ждут. Следите за глубиной очереди и TTFT, а не за ощущениями.
Ставьте лимиты и выше по стеку: реверс-прокси с лимитом соединений падает быстро и честно, где глубокая очередь падает медленно и загадочно.
Планирование ёмкости, грубо
Что стоит запрос сверх весов — KV-кэш, на токен, на весь его контекст:
KV байт/токен ≈ 2 × слои × kv_heads × head_dim × bytes_per_value
Разбор, типичная плотная модель 8B (32 слоя, 8 KV-голов, размер головы 128, значения fp16):
2 × 32 × 8 × 128 × 2 B ≈ 131 KB на токен → разговор на 2k токенов ≈ 256 MiB KV → десять таких чатов ≈ 2.5 GB поверх ~5 GB весов.
(двойка — это K и V; см. KV-кэш). Агенты с длинным контекстом поэтому куда дороже на запрос, чем чат. Грубая таблица комфорта, с тяжёлыми оговорками — контексты чат-длины, только порядок величины:
| GPU | Модель | Грубо одновременных лёгких чатов |
|---|---|---|
| 8 GB | 7–8B Q4 | 1–3 |
| 24 GB | 8B Q4 | ~10–30 |
| 24 GB | 32B Q4 | ~3–8 |
| 48 GB | 70B Q4 | ~5–15 |
Мерьте со своими реальными промптами: длина контекста двигает эти числа сильнее любой другой переменной.
Сигналы, что вы насыщены
- TTFT растёт от миллисекунд к секундам по мере дня.
- Глубина очереди устойчиво выше нуля; запросы ждут до первого токена.
- tok/s на поток провисает по мере набивания батчей.
- Логи vLLM показывают вытеснение или пересчёт KV — давление памяти, а не медлительность модели.
Любое из этого: ограничьте контекст, квантуйте KV-кэш или добавьте GPU — в этом порядке дешевизны — пока ваши пользователи не начали громко жаловаться.
Графируйте TTFT и глубину очереди через день — устойчивый рост означает нехватку ёмкости, пила означает всплески, которые можно поглотить очередью.
Аренда против покупки, эскиз
На момент написания арендованная GPU класса 24GB стоит примерно $0.50–2/час, а б/у карта 24GB — несколько сотен долларов; точка безубыточности где-то около 300–1,000 часов фактической генерации. Локальное побеждает по приватности, задержке и отсутствию лимитов (рамка в почему локально); аренда побеждает при взрывном спросе и моделях класса 80GB, которых не держит ни одна потребительская карта (см. математику VRAM насчёт того, что где влезает). Большинство людей переоценивает свои часы — инструментируйте реальную неделю, прежде чем решать.
Связанные разделы: скорость · KV-кэш · развёртывание эндпоинта · математика VRAM