vLLM, TGI и SGLang: сравнение для продакшена без шума
vLLM, TGI и SGLang: разбор по памяти, throughput, задержке и ежедневной эксплуатации, чтобы выбрать стек под SLA, бюджет и состав команды.

Почему выбор сервера инференса быстро становится проблемой
На пилоте почти любой сервер инференса LLM выглядит приемлемо. Одна модель, один GPU, короткие запросы, мало параллельности. В продакшене все меняется быстро, и первая поломка обычно приходит не там, где ее ждали.
Чаще всего сначала заканчивается VRAM. Модель спокойно загружается, проходит тесты и держит несколько сессий. Потом приходят длинные диалоги, растет контекст, копится KV cache, включается батчинг, и память уходит быстрее, чем видно по сухой оценке размера весов. Команда смотрит на карту с 80 ГБ и думает, что запас еще большой. На практике его может не быть уже к середине дня.
Если память держится, всплывает очередь. Самый неприятный сценарий выглядит так: средняя задержка еще терпимая, а хвост SLA уже развалился. Один пользователь получает первый токен быстро, другой ждет заметно дольше, потому что сервер иначе собирает батчи, режет запросы и освобождает память. Поэтому vLLM, TGI и SGLang при одной и той же модели ведут себя по-разному.
Разница чаще не в названии фреймворка, а в деталях работы. Один стек лучше переживает поток коротких запросов. Другой увереннее чувствует себя на длинной генерации. Третий показывает красивый throughput в бенчмарке, но начинает вести себя неровно на смешанной нагрузке, где рядом идут чат, суммаризация и RAG. Для команды это всегда выглядит досадно: модель та же, GPU те же, а цифры и поведение в проде уже другие.
После пилота время уходит не на команду запуска контейнера. Оно уходит на мелкие, но дорогие вопросы: почему сервер внезапно ловит OOM, почему метрики не объясняют скачок задержки, почему обновление версии ломает квантованную модель, почему совместимость с OpenAI есть формально, а часть параметров работает не так, как ждут разработчики.
Ошибка выбора быстро бьет по бюджету. Если стек плохо расходует память или слабо держит параллельность, команда покупает лишние GPU не потому, что модель этого требует, а потому что сервер теряет емкость под реальной нагрузкой. Потом страдает и SLA: приходится резать контекст, ограничивать число одновременных сессий или держать слишком большой запас железа. В продакшене это уже не мелочь, а постоянный счет за инфраструктуру и лишние инциденты у команды эксплуатации.
Как честно сравнивать vLLM, TGI и SGLang
Сравнение ломается в тот момент, когда команда меняет сразу несколько переменных. Если на vLLM, TGI и SGLang запускать разные модели, разную квантование или разный контекст, цифры почти ничего не значат. Вы сравниваете не сервер, а весь набор условий.
Честный тест начинается скучно. Нужно зафиксировать одну и ту же модель, один размер контекста и один формат весов. Если на одном стенде стоит FP16, а на другом AWQ или GPTQ, разница в памяти и скорости будет связана не столько с движком, сколько с квантованием. То же самое с длиной контекста: при 8K и 32K сервер ведет себя уже заметно иначе.
До первого прогона полезно записать базовые условия и не трогать их до конца теста: ту же GPU и тот же объем VRAM, одинаковые версии CUDA, драйвера и контейнера, один профиль нагрузки и одинаковые параметры генерации. Такой журнал выглядит занудно, но именно он спасает от ложных выводов. Разница в 10-15% часто исчезает сразу после того, как команда находит другой драйвер или старый контейнерный образ.
Отдельно меряйте prefill и decode. Это две разные фазы, и они нагружают сервер по-разному. Prefill сильнее зависит от длины входа и чаще давит на память и пропускную способность. Decode чувствителен к batch size, планировщику и работе с KV cache. Если смешать их в одну цифру, вы не поймете, где стек хорош, а где начинает проседать.
По задержке не смотрите только на среднее. Средняя метрика приятно выглядит в отчете, но пользователи замечают хвосты. Смотрите хотя бы на три числа: среднюю задержку, p95 и p99. Если среднее хорошее, а p99 уходит далеко вверх, в продакшене это превращается в зависания, таймауты и жалобы от соседних команд.
Еще один частый промах - сравнение на разных GPU. Даже соседние карты из одной линейки могут дать другую картину по памяти и throughput. Если железо разное, честнее назвать это отдельным тестом, а не прямым сравнением.
Нормальный результат выглядит скучно: одна модель, одна GPU, понятный профиль нагрузки, раздельные метрики по prefill и decode, записанные версии окружения. Зато после такого теста уже видно, где vLLM, TGI и SGLang дают реальную разницу, а где спор идет о настройках.
Куда уходит память
VRAM съедают не только веса модели. В проде память обычно уходит сразу в три места: в сами веса, в KV cache и в служебные буферы рантайма. В последнюю группу попадают временные тензоры, память под батчинг, загрузчик и прочие накладные расходы.
Больше всего обычно недооценивают KV cache. На коротком запросе все выглядит спокойно: модель загрузилась, ответы идут, запас VRAM есть. Потом пользователи начинают вести длинные диалоги, добавляют большие системные промпты, прикладывают фрагменты документов, и память растет заметно быстрее, чем ожидала команда.
Поэтому смотреть нужно не только на память сразу после старта, а на память через 20-30 реплик в одном сеансе. Для внутреннего ассистента в банке или телекоме это обычный режим: длинный контекст, цитаты из документов, несколько уточнений подряд. В такой нагрузке узкое место часто не в весах, а именно в кеше внимания.
Paged attention помогает там, где длины запросов сильно отличаются и батчи постоянно меняются. На практике vLLM часто выигрывает в таких условиях: он аккуратнее упаковывает память и меньше теряет на фрагментации. Но это не магия. Если все запросы очень длинные и KV cache уже раздулся, paged attention не уберет сам объем данных. Он просто расходует память аккуратнее.
TGI обычно проще читать и профилировать, но под длинные батчи запас VRAM нужно считать строже. SGLang тоже может вести себя экономно, особенно если у вас много повторяющихся префиксов и вы используете его механизмы кеша. Рост KV cache он тоже не отменяет.
Отдельно сбивает с толку tensor parallel. Он позволяет разложить веса по нескольким GPU, но добавляет обмен между картами и свои буферы. Иногда модель уже помещается, а throughput не растет, потому что память вы спасли, а коммуникации стали дороже.
Квантование меняет картину, но не полностью. Оно хорошо режет память на веса, иногда очень заметно. KV cache при этом уменьшается не всегда так сильно, как ждут. Из-за этого 4-bit модель может отлично запускаться на старте и все равно упираться в VRAM под длинной сессией.
Если говорить грубо, короткие запросы чаще упираются в организацию батча и служебные буферы, а длинные - в KV cache. Поэтому честный тест сервера инференса должен включать оба режима. Иначе вывод по памяти получится слишком красивым.
Что происходит с throughput и задержкой
Одинаковый тест на одной GPU часто рисует удобную, но неполную картину. Один короткий запрос почти любой сервер обработает быстро, и разница между vLLM, TGI и SGLang покажется небольшой. В продакшене все меняется, когда одновременно приходят десятки запросов, а ответы у них разной длины.
Одиночный запрос и очередь под нагрузкой
На одиночных запросах обычно важнее prefill и время до первого токена, чем пиковые tokens/s. Если пользователь ждет первую букву 2-3 секунды, ему уже не так важно, что потом модель печатает быстро.
Под нагрузкой важен не рекорд на графике, а то, как сервер держит очередь. vLLM часто выигрывает на высоком concurrency за счет continuous batching и более экономной работы с KV cache. Он лучше загружает GPU, когда в системе много параллельных чатов с короткими и средними ответами.
TGI чаще ведет себя ровнее на умеренной нагрузке и обычно проще для команды, которой нужен понятный путь к эксплуатации. Но если трафик становится плотным, а запросы сильно отличаются по длине, очередь может начать расти раньше, чем команда ожидала. Тогда средняя скорость еще выглядит нормально, а p95 уже начинает раздражать пользователей.
SGLang иногда показывает очень сильный throughput, особенно на сценариях с повторяемыми шаблонами запросов и более сложной логикой вокруг генерации. На смешанном трафике результат сильнее зависит от профиля нагрузки. Если короткие чаты, длинные отчеты и streaming идут через один пул, разброс задержки может стать заметным.
Чтобы увидеть реальную картину, мало одного показателя. Обычно достаточно смотреть на время до первого токена, inter-token latency при streaming, p95 и p99 на 8, 32 и 64 параллельных запросах и просадку скорости на длинной генерации, например на 1000+ токенов.
Streaming и длинная генерация
Streaming стоит мерить отдельно. Пользователь замечает две вещи: как быстро пришел первый токен и есть ли паузы во время вывода. Сервер может показывать высокий общий throughput, но отдавать токены рвано. Для чата это хуже, чем немного меньшая скорость без пауз.
Длинная генерация почти всегда портит картину под общей нагрузкой. Один запрос на большой ответ дольше занимает decode-слоты и заставляет короткие запросы ждать. vLLM в таких условиях часто держится лучше, но и ему нужны лимиты на длину ответа и нормальная настройка batch. TGI чаще требует более жестких ограничений на max_new_tokens. SGLang лучше проверять именно на своих сценариях: на однородных задачах он может быть очень быстрым, на смеси трафика - менее ровным.
Если тест показывает 220 tokens/s, но время до первого токена уже 3 секунды, а p95 на 40 одновременных запросах уходит к 15 секундам, сервис нельзя назвать быстрым. Для продакшена важна не пиковая цифра, а предсказуемая скорость для всей очереди.
Что различается в ежедневной эксплуатации
На тестовом стенде почти любой сервер выглядит терпимо. Настоящая разница появляется на второй неделе, когда команда обновляет образ, ловит редкий OOM и пытается откатиться без ночного созвона.
vLLM обычно проще всего запустить. У него знакомый интерфейс, совместимый с OpenAI, поэтому многие клиенты поднимаются без переписывания кода. Обновления тоже проходят спокойно, если вы фиксируете версию образа и ревизию модели. Потом начинается тонкая настройка: размер KV cache, параллелизм, batching, лимиты контекста. На старте это легко, в эксплуатации уже сложнее.
TGI часто нравится командам, которым нужен более предсказуемый режим работы. Контейнеры, sharding и rollback у него обычно понятны, метрики тоже удобно читать. Минус в другом: если у вас парк клиентов уже заточен под OpenAI API, мелкие отличия в запросах и ответах лучше проверить заранее, а не в день релиза.
SGLang часто выбирают за гибкость и скорость развития. Платой становится время команды. Обновление версии может принести не только прирост, но и новый класс странных падений. Разбор проблем обычно глубже: нужно понимать scheduler, memory planning и особенности конкретной модели. Если в команде нет человека, который любит копаться в таких вещах, усталость приходит быстро.
Когда моделей несколько, все три инструмента удобнее держать по схеме "одна модель - один сервис". Так проще обновлять версии и ловить регрессии. Попытка сложить много разных моделей в один процесс почти всегда бьет по предсказуемости.
Если клиентам нужен единый вход, совместимый с OpenAI, этот слой лучше вынести отдельно от серверов инференса. В российских командах такую роль иногда берет RU LLM: он дает единый endpoint, а логи, бэкапы и аудит запросов остаются внутри РФ, что удобно там, где есть требования по 152-ФЗ и хранению данных.
По опыту продакшена, меньше всего ручной настройки обычно нужно у vLLM на старте и больше всего у SGLang после запуска. TGI часто оказывается посередине. Если у вас маленькая команда без отдельного SRE под LLM, это может повлиять на выбор сильнее, чем разница в бенчмарке на 10-15%.
Как выбрать стек шаг за шагом
Сравнивать vLLM, TGI и SGLang по чужим графикам почти бесполезно. Один и тот же сервер ведет себя по-разному на другой модели, другой длине промпта и другой очереди запросов. Нормальный выбор начинается не с бенчмарка, а с ваших ограничений.
Сначала зафиксируйте рамки: какой SLA нужен по p95 и p99, сколько денег можно тратить на GPU в месяц, какие карты уже есть и сколько VRAM доступно, можно ли выносить данные наружу или все должно остаться в РФ. После этого спор обычно быстро сужается.
Дальше выберите 2-3 типовых сценария нагрузки. Нужен не "средний запрос", а живые случаи: короткий чат, длинный RAG-запрос, пакетная генерация ночью. Если у вас 80% трафика идет на ответы до 300 токенов, не стоит принимать решение по тесту на 8K контексте.
Потом соберите короткий стенд на одной модели. Не меняйте сразу все: одна и та же модель, одно и то же квантование, одинаковые параметры генерации, одинаковый набор запросов. Так вы поймете, где разница идет от стека, а где вы сами сдвинули условия.
Смотрите не только на tokens/s. Минимальный набор метрик простой: VRAM в простое и под нагрузкой, latency на p50, p95 и p99, throughput при росте параллелизма и ошибки вроде OOM, таймаутов и зависших воркеров.
Потом проверьте скучные вещи. Именно они чаще ломают пилот. Как сервер стартует после обновления драйвера? Сколько занимает rollback? Что происходит после ночного рестарта и прогрева? Если стек дает чуть лучший throughput, но требует ручной магии при каждом релизе, цена такой победы быстро становится слишком высокой.
Только после этого оставляйте кандидата под недельным наблюдением. Пусть он переживет рабочий день, вечерний пик, фоновые джобы и хотя бы один перезапуск. За неделю обычно всплывают утечки памяти, странные хвосты по задержке и редкие ошибки батчинга, которых не видно на часовом тесте.
Ошибки, которые ломают пилот
Самая частая ошибка банальна: команде кажется, что она сравнивает vLLM, TGI и SGLang, хотя на деле сравнивает разные квантизации. Если один стенд работает в FP16, второй в AWQ, а третий в GPTQ, общий вывод почти ничего не стоит. Вы смешали качество ответа, расход памяти и скорость в один клубок.
Вторая ловушка - смотреть только на tokens/s. Для отчета это удобно, для живого сервиса почти бесполезно. Пользователь чувствует не среднюю скорость, а паузы, зависания и длинные хвосты. Поэтому без p95 и p99 легко выбрать стек, который выглядит быстрым на коротком тесте и раздражает людей в реальной работе.
Длинный контекст ломает больше пилотов, чем слабый бенчмарк. На коротких запросах почти любой сервер выглядит бодро. Но как только вы добавляете историю диалога, фрагменты документов и ответ на 700-1000 токенов, память уходит в KV cache. После этого внезапно меняется все: число одновременных запросов, задержка и частота OOM.
Отдельная ошибка - тестировать на игрушечных промптах. Внутренний чат-ассистент для банка редко живет в мире запросов на 100 токенов. У него есть длинные системные инструкции, куски регламентов, маскирование PII и требования к логам. Если пилот не повторяет такую нагрузку, он почти всегда показывает слишком красивую картину.
Еще один недооцененный расход - эксплуатация. Команды часто не считают, сколько времени уходит на обновления, отладку странных падений, настройку мониторинга и разбор регрессий после смены версии модели или CUDA. Сервер, который дает чуть больше скорости на графике, может забирать лишний день инженера каждую неделю.
Хорошая защита от этих ошибок простая: зафиксировать одну модель и одно квантование, прогнать реальные размеры входа и ответа, смотреть на p50, p95, p99, время до первого токена и частоту OOM, а еще отдельно оценить время на обновление, диагностику и возврат к прошлой версии. Честный пилот почти всегда выглядит скромнее. Зато после него не приходится заново пересобирать весь стенд перед запуском.
Пример: внутренний чат-ассистент для банка
Днем у банка обычно идет простой поток: сотрудники спрашивают про лимиты, статусы заявок, внутренние регламенты, шаблоны писем. Эти запросы короткие, и людям нужна живая реакция без пауз. Вечером картина меняется: системы и команды просят длинные сводки по инцидентам, продажам, обращениям и рискам.
Такой смешанный трафик быстро показывает разницу между стеками. Сервер, настроенный под быстрый чат, может просесть на длинных ответах. Стек, который хорошо переваривает вечерние сводки, днем может дать лишнюю задержку на коротких вопросах.
У банка есть еще одна проблема: часть запросов содержит персональные данные. Значит, смотреть нужно не только на память и throughput. Нужны маскирование PII, понятные логи, аудит запросов и размещение данных там, где это позволяют внутренние правила и 152-ФЗ.
Для пилота на 1-2 GPU многие команды сначала ставят vLLM. Он часто лучше переживает смешанную нагрузку и экономнее расходует память на живом чате. Если нужны длинные структурированные ответы, сложные цепочки генерации или программный контроль декодирования, рядом стоит проверить SGLang. TGI в таком сценарии выглядит спокойным вариантом для команд, которые уже живут в экосистеме Hugging Face и хотят знакомый путь запуска, но на смешанном трафике ему чаще нужна более аккуратная разводка очередей.
Требование к единому входу, совместимому с OpenAI, меняет саму схему. Не обязательно выбирать один сервер на все случаи. Банк может оставить один API для всех внутренних клиентов, не трогать SDK и промпты, а за этим входом маршрутизировать быстрые дневные запросы в один контур, а вечерний batch - в другой.
На практике это часто лучше, чем пытаться заставить один сервер одинаково хорошо обслуживать кассира днем и аналитический batch ночью.
Быстрая проверка перед запуском
Перед боевым стартом мало прогнать пару удачных запросов. Сервер инференса должен спокойно пережить длинную смену, когда трафик идет волнами, контекст у запросов разный, а очередь то растет, то падает. Для vLLM, TGI и SGLang тест сложно считать пройденным, пока стек не отработает хотя бы 8 часов подряд без утечек памяти, скачков задержки и странных ошибок после нескольких перезагрузок воркера.
Полезно смотреть не только на средние цифры. Средняя задержка часто выглядит прилично даже тогда, когда хвост уже едет вверх. Если модель укладывается в норму днем, но вечером начинает копить очередь, проблема уже есть, даже если средний throughput на графике кажется хорошим.
Для алертов обычно хватает нескольких сигналов: длины очереди и времени ожидания до начала генерации, OOM на GPU и роста фрагментации памяти, резкого скачка p95 и p99, числа перезапусков процесса и неуспешных запросов, а еще заполнения диска, если сервер пишет подробные логи.
Отдельно проверьте релизы. Команда должна уметь поменять версию сервера, драйвера или самой модели за минуты, а не за полдня. Хороший признак простой: старая сборка сохранена, конфиг версионируется, health check понятный, а трафик можно быстро вернуть на прошлую версию без ручной магии.
С бюджетом ошибка встречается постоянно: его считают по обычному дню. В продакшене платят за пик. Если внутренний ассистент молчит утром и загружает GPU после обеда, ориентируйтесь на худший час, а не на среднее число токенов за сутки. Иначе сервер держится только на бумаге.
С логами тоже лучше договориться заранее. Если в запросах есть персональные данные, нельзя бездумно писать промпты и ответы целиком. Для российских команд это быстро упирается в 152-ФЗ, место хранения логов и маскирование PII. Если у вас есть внешний API-слой перед серверами инференса, заранее проверьте, где лежат логи и бэкапы, кто видит сырые данные и как вытащить аудит по конкретному запросу.
Нормальный финальный критерий простой: система держит длинный тест, алерты срабатывают вовремя, откат работает, пик бюджета сходится, а политика логов не создает лишний риск. Если хотя бы один пункт висит в воздухе, запуск лучше сдвинуть на день, чем потом чинить ночь напролет.
Что делать дальше
Не покупайте GPU и не утверждайте стек по чужим графикам. Для продакшена нужна своя таблица тестов: одна и та же модель, одинаковый контекст, фиксированный batch, одинаковая квота по токенам и понятный p95 по задержке. Иначе сравнение быстро превращается в спор о настройках, а не о результате.
Такую таблицу лучше собрать до закупки железа. В ней обычно хватает 6-8 сценариев: короткий чат, длинный контекст, высокий параллелизм, длинная генерация, пик нагрузки и аварийный рестарт. Уже на этом шаге видно, где стек ест лишнюю память, где держит throughput, а где команда потом будет тратить часы на ручную отладку.
Перед финальным выбором полезно зафиксировать несколько решений. Назначьте один основной стек под низкую задержку и один запасной на случай, если обновление драйвера, CUDA или самой модели ломает привычный путь. Оставьте клиентский контракт стабильным: если вход совместим с OpenAI, вы сможете менять модель, сервер инференса и даже base_url без переписывания клиентских приложений. Заранее решите, где лежат логи и бэкапы. Для команд с требованиями по 152-ФЗ это часть архитектуры, а не задача "на потом".
Еще один практичный шаг - отделить слой маршрутизации от бизнес-логики. Клиенты не должны знать, какая именно модель отвечает сегодня. Пусть они работают с одним API, а выбор модели, fallback и лимиты живут отдельно. Тогда переход с одной модели с открытыми весами на другую займет часы, а не спринт.
Хороший следующий шаг выглядит просто: свой бенчмарк, два проверенных стека, заранее определенное хранение логов и бэкапов и такой API-контракт, который переживет смену модели без переделки клиентов.
Часто задаваемые вопросы
С чего начать выбор между vLLM, TGI и SGLang?
Сначала зафиксируйте свои рамки: какой p95 и p99 вам нужен, сколько VRAM уже есть, какой пик параллельности ждете и можно ли держать данные только в РФ. Потом прогоните одну и ту же модель, одно и то же квантование и один набор запросов на всех трех стеках. Без этого сравнение быстро ломается.
Почему модель помещается в VRAM, а потом все равно ловит OOM?
Потому что VRAM занимают не только веса. Длинные диалоги раздувают KV cache, батчинг добавляет буферы, и запас памяти тает уже в живой очереди, а не на старте процесса.
Какие метрики смотреть кроме tokens/s?
Смотрите не только на tokens/s. Для живого сервиса полезнее время до первого токена, p95 и p99, память в простое и под нагрузкой, длина очереди и число OOM или таймаутов.
Когда vLLM чаще всего оказывается лучшим выбором?
Обычно vLLM хорошо держит высокий параллелизм, смешанный чатовый трафик и длинные сессии, где память начинает жить своей жизнью. Если вам нужен быстрый старт и OpenAI-совместимый интерфейс без лишней переделки клиентов, его часто берут первым кандидатом.
В каких случаях разумно выбрать TGI?
TGI часто берут команды, которым нужен более понятный путь в эксплуатацию и знакомая экосистема Hugging Face. Он нередко ведет себя ровно на умеренной нагрузке, но на длинных и сильно разных запросах ему стоит заранее проверить хвосты задержки.
Когда имеет смысл тестировать SGLang?
SGLang стоит пробовать, когда у вас сложная логика вокруг генерации, повторяющиеся префиксы или длинные структурированные ответы. Он может дать очень хорошие цифры, но обычно просит больше внимания к версиям, планировщику и памяти.
Насколько квантование реально спасает от нехватки памяти?
Квантование заметно режет память на веса и помогает запустить модель на более скромном железе. Но оно не решает проблему длинных сессий полностью, потому что KV cache все равно продолжает расти.
Нужен ли отдельный API-слой перед сервером инференса?
Да, это часто упрощает жизнь. Клиенты работают с одним API, а вы отдельно меняете модели, лимиты и сами серверы инференса; для российских команд еще полезно сразу решить, где лежат логи, бэкапы и аудит по 152-ФЗ. Если нужен единый вход с хранением данных внутри РФ, такую роль может взять RU LLM.
Стоит ли держать несколько моделей в одном процессе?
Лучше не делать так без сильной причины. Одна модель в одном сервисе упрощает обновления, откат, поиск регрессий и чтение метрик; когда вы складываете много разного в один процесс, предсказуемость быстро падает.
Какой пилот даст честное сравнение перед запуском?
Хватает короткого, но честного пилота на 6–8 сценариев: короткий чат, длинный контекст, высокая параллельность, длинная генерация, пик нагрузки и перезапуск. Дайте стенду пожить хотя бы несколько часов подряд и проверьте, как он ведет себя после рестарта, а не только на удачных первых запросах.