
Лучший способ сжечь бюджет компании на инфраструктуру — запуск LLM в продакшене. Но только если вы не знаете, какой бэкенд использовать и как его настраивать.
Проблема в том, что параметры современных нейросетей растут по экспоненте, а обрабатываемый контекст становится все длиннее. Из-за этого операционные расходы на генерацию текста превращаются в главный барьер для масштабирования сервисов. Обслуживать запросы к LLM в десятки раз сложнее и дороже, чем крутить классический поиск по ключевым словам. Чтобы не разориться на счетах и выжать максимум из дорогостоящих GPU, нужен специализированный софт.
В этой статье мы подробно сравним три ведущих бэкенда для инференса: vLLM, LMDeploy и экосистему NVIDIA Triton Inference Server в связке с TensorRT-LLM. Мы разберем, как они управляют памятью, какие методы квантования используют и как распределяют запросы, а также оценим результаты их бенчмарков на актуальных архитектурах NVIDIA Hopper и Blackwell.
Чтобы понять архитектурные решения современных движков инференса, необходимо разобрать механику авторегрессионной генерации в моделях на базе архитектуры Transformer. Процесс инференса разделяется на две асимметричные фазы, каждая из которых создает свой профиль нагрузки на компоненты графического процессора.
Первая фаза, известная как Prefill — обработка промпта или контекста. Она заключается в параллельном вычислении представлений для всех слов (токенов) пользовательского запроса. Математически все сводится к огромному количеству умножений матриц.
Эта фаза эффективно утилизирует матричные тензорные ядра GPU и характеризуется высокой арифметической интенсивностью. Видеокарта на данном этапе работает на полную мощность, а главным ограничивающим фактором выступает чистая вычислительная сила чипа.
Вторая фаза — Decode — авторегрессионная генерация. По своей природе она принципиально отличается от первой. Генерация происходит строго последовательно: каждый новый токен вычисляется на основе всех предыдущих.
Для генерации одного токена система вынуждена загружать все веса модели из глобальной видеопамяти (плашек High Bandwidth Memory или же HBM) в регистры потоковых мультипроцессоров (уровней L1–L3 Static Random Access Memory, далее SRAM). Поскольку объем вычислений на каждый байт загруженных данных минимален, фаза генерации жестко ограничена пропускной способностью памяти, оставляя вычислительные блоки GPU в состоянии простоя значительную часть времени.

Для общего понимания, отражу здесь общий процесс, где конкретно располагаются Prefill и Decode.
Перед началом логического вывода архитектура модели и ее вычисленные весовые коэффициенты загружаются с диска или удаленного хранилища в видеопамять GPU. Этот шаг выполняется один раз, перед обработкой любых запросов. В принципе, модель должна быть загружена с состоянием всех слоев, готовых к приему входных токенов.
Вы отправляете в модель свой запрос. В частности, это текст, если мы говорим о чистых text-to-text моделях без дополнений в виде обработки аудио, видео и картинок.
Модель использует свой токенизатор для разделения вашего текста на токены (слова, подслова или специальные символы) и сопоставляет каждый токен с уникальным идентификатором (целым числом), используя файлы токенизатора и vocab из артефактов модели.
Каждый идентификатор токена используется для поиска изученного вектора внедрения из матрицы внедрения модели, которая также сохраняется среди артефактов модели. Эти векторы представляют токены таким образом, который может обработать нейронная сеть.
Фаза предварительного заполнения, или Prefill, о котором как раз и шла речь чуть выше, происходит при однократной обработке моделью входных токенов. В этот момент она использует загруженные веса, чтобы настроить свое внутреннее состояние для генерации. При предварительном заполнении сохраняются тензоры ключей и значений для всех входных токенов. На последующих этапах декодирования они используются заново, чтобы избежать повторных вычислений по запросу.
И второй рассмотренный ранее этап — это декодирование, когда модель генерирует выходные токены один за другим. На каждом шаге она использует текущий контекст для прогнозирования следующего токена. Текущий контекст также сохраняется в кэше KV.
В дополнение к каждому шагу декодирования модель также присваивает значения вероятности возможным следующим токенам. Она использует параметры выборки (например, температуру, top_k, top_p), чтобы выбрать один токен из наиболее вероятных кандидатов. Это определяет, насколько креативным или целенаправленным будет результат.
Когда полностью завершился этап создания выходных токенов, они обратно преобразуются в читаемый текст с использованием словаря токенизатора и мы получаем окончательный текст ответа.
Рабочий процесс повторяет шаги декодирования до тех пор, пока не будет сгенерировано желаемое количество токенов или не будет выполнено специфическое условие остановки.

Каталог готовых ИИ-моделей
Сервис для запуска и управления LLM в облаке Selectel. Выберите модель, конфигурацию и получите готовый эндпоинт для работы с ней.
Вернемся к одному из ключевых механизмов оптимизации авторегрессионного декодирования, а именно к KV кэшу. Сохранение тензоров ключей и значений предотвращает необходимость пересчета всей последовательности на каждом шаге.
Для понимания расчета, я подобрал очень удобный скрипт-пример:
import numpy as np
# Простые эмбеддинги для демонстрации
embeddings = {
'The': np.array([1, 0, 0, 0]),
'quick': np.array([0, 1, 0, 0]),
'brown': np.array([0, 0, 1, 0]),
'fox': np.array([0, 0, 0, 1]),
'jumped': np.array([1, 1, 0, 0])
}
# Матрицы весов (упрощенные)
W_Q = W_K = W_V = np.array(
[[1, 0],
[0, 1],
[0, 0],
[0, 0]]
)
def compute_attention(self, input_words):
# Преобразуем слова в эмбеддинги
E = np.array([embeddings[word] for word in input_words])
# Вычисляем K, V для всех токенов
K = E @ W_K # Размерность: (seq_len, 2)
V = E @ W_V # Размерность: (seq_len, 2)
# Вычисляем Q для последнего токена
Q = E[-1] @ W_Q # Размерность: (1, 2)
# Вычисляем масштабированные оценки внимания (scores)
scale = np.sqrt(2) # квадратный корень из размерности ключей/запросов (в данном случае 2)
scores = (Q @ K.T) / scale # Размерность: (1, seq_len)
# Применяем softmax для получения весов внимания
attention_weights = self.softmax(scores) # Размерность: (1, seq_len)
# Применяем веса внимания к значениям (values)
output = attention_weights @ V # Размерность: (1, 2)
return outputНиже приведен пример работы шага Decode с использованием и без KV-кэша поэтапно.

Требуемый объем памяти для KV-кэша можно описать следующим математическим выражением:

Здесь N_layers обозначает количество слоев трансформера, N_heads — количество голов внимания, D_head — размерность отдельной головы, L_sq — длину последовательности (включая контекст и сгенерированные токены), B — размер батча (количество одновременных запросов), а P_bytes — точность представления данных в байтах. Например, для базовых у большинства моделей FP16 и BF16 типов — это два байта.
В ранних системах инференса, таких как FasterTransformer, память для KV-кэша выделялась в виде непрерывных линейных буферов, рассчитанных на максимально возможную длину последовательности, заданную гиперпараметрами модели. Подобный подход приводил к катастрофической деградации эффективности утилизации памяти из-за двух форм фрагментации.
Внутренняя фрагментация возникала, когда запрос завершался задолго до достижения максимальной длины, оставляя заранее зарезервированную память пустой. Внешняя фрагментация формировалась из-за различной длины запросов, создавая в памяти пробелы, размер которых был недостаточен для размещения новых непрерывных буферов. Глубокое профилирование традиционных систем показало, что лишь от 20,4% до 38,2% выделенной видеопамяти фактически содержало полезные состояния токенов, тогда как остальной объем расходовался впустую. Это ограничение не позволяло увеличивать размер батча, критически снижая пропускную способность сервера.
Ниже можно увидеть пример с резервированием места в памяти для одного запроса из 8 токенов с ограничением в 2 048.

Как видно из рисунка, совсем небольшой запрос оставил 2 038 резервных слотов пустовать, неэффективно занимая при этом ячейки памяти.
Проект vLLM, изначально разработанный в Лаборатории Sky Computing Калифорнийского университета в Беркли, произвел фундаментальный сдвиг в индустрии, представив архитектуру управления памятью PagedAttention.
О ней более подробно можно более подробно почитать в статье от самих разработчиков, либо в ее более кратком изложении.
Решение напрямую адаптирует классические принципы виртуальной памяти и страничной организации из операционных систем для работы с тензорами внимания.
Алгоритм PagedAttention полностью отказывается от требования хранения KV-кэша в непрерывных участках видеопамяти. Вместо этого память квантуется на небольшие блоки-страницы фиксированного размера. Каждый из которых способен хранить ключи и значения для определенного количества токенов — обычно 16 токенов на блок.
Такой размер эквивалентен примерно 12,8 КБ для модели класса 13B и является оптимальным компромиссом: он достаточно мал для минимизации потерь, но достаточно велик для эффективного запуска CUDA-ядер.

Для маршрутизации видеопамяти vLLM поддерживает таблицу блоков, функционирующую аналогично таблице страниц виртуальной памяти. Она динамически транслирует логические позиции токенов запроса в адреса разрозненных физических блоков в HBM видеокарты.
Эта архитектура приносит ряд качественных улучшений:
внешняя фрагментация исключается математически, поскольку все физические блоки памяти имеют идентичный размер и могут быть выделены любому процессу;
внутренняя фрагментация локализуется исключительно в рамках последнего, возможно, не полностью заполненного блока последовательности, что сводит потери памяти к околонулевым значениям;
табличная маршрутизация позволяет нескольким логическим последовательностям ссылаться на одни и те же физические блоки памяти.
Ниже представлен код и лог сравнительного анализа со стандартной библиотекой Hugging Face Transformers и использование платформы vLLM, показавший двукратное увеличение количества запросов в секунду по сравнению с традиционным методом пакетного вывода.
import time
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from vllm import LLM, SamplingParams
# Load model and tokenizer (traditional way)
model_name = "facebook/opt-1.3b"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
# Add padding token
tokenizer.pad_token = tokenizer.eos_token
def generate_traditional(prompts, max_new_tokens=50):
"""Traditional generation - processes one by one"""
results = []
start_time = time.time()
for prompt in prompts:
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.7,
pad_token_id=tokenizer.eos_token_id,
do_sample=True
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
results.append(response)
end_time = time.time()
return results, end_time - start_time
# Test prompts
system_prompts = """
You are an expert AI tutor. Explain concepts clearly and simply using analogies when helpful. Ask guiding questions to help students understand rather than giving direct answers. Provide relevant examples and encourage critical thinking. Be patient, supportive, and adapt your teaching style to each student's learning pace and specific needs.
"""
test_prompts = [
"The future of AI is",
"Climate change requires",
"Space exploration helps",
"Education technology can",
"Renewable energy sources"
]
test_prompts = [system_prompts + prompt for prompt in test_prompts]
print("=== Traditional Method ===")
traditional_results, traditional_time = generate_traditional(test_prompts)
print(f"Time taken: {traditional_time:.2f} seconds")
print(f"Requests processed: {len(test_prompts)}")
print(f"Throughput: {len(test_prompts)/traditional_time:.2f} requests/second")
print()
def generate_traditional_batched(prompts, max_new_tokens=50):
"""Traditional batched generation"""
start_time = time.time()
# Tokenize all prompts
inputs = tokenizer(
prompts,
return_tensors="pt",
padding=True,
truncation=True
).to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.7,
pad_token_id=tokenizer.eos_token_id,
do_sample=True
)
results = []
for output in outputs:
response = tokenizer.decode(output, skip_special_tokens=True)
results.append(response)
end_time = time.time()
return results, end_time - start_time
print("=== Traditional Batched Method ===")
traditional_batch_results, traditional_batch_time = generate_traditional_batched(test_prompts)
print(f"Time taken: {traditional_batch_time:.2f} seconds")
print(f"Requests processed: {len(test_prompts)}")
print(f"Throughput: {len(test_prompts)/traditional_batch_time:.2f} requests/second")
print()
del model
# Initialize vLLM engine
print("=== vLLM Method ===")
llm = LLM(
model=model_name,
dtype="float16",
max_model_len=2048, # Adjust based on your GPU memory
gpu_memory_utilization=0.8
)
# Set sampling parameters
sampling_params = SamplingParams(
temperature=0.7,
max_tokens=50,
top_p=0.9
)
def generate_vllm(prompts):
"""vLLM generation - optimized batching"""
start_time = time.time()
outputs = llm.generate(prompts, sampling_params)
results = []
for output in outputs:
response = output.outputs[0].text
results.append(output.prompt + response)
end_time = time.time()
return results, end_time - start_time
# Same test prompts
vllm_results, vllm_time = generate_vllm(test_prompts)
print(f"Time taken: {vllm_time:.2f} seconds")
print(f"Requests processed: {len(test_prompts)}")
print(f"Throughput: {len(test_prompts)/vllm_time:.2f} requests/second")
print()=== Traditional Method ===
Time taken: 5.53 seconds
Requests processed: 5
Throughput: 0.90 requests/second
=== Traditional Batched Method ===
(...часть логов опущена...)
Time taken: 1.07 seconds
Requests processed: 5
Throughput: 4.68 requests/second
=== vLLM Method ===
(...часть логов опущена...)
Time taken: 0.51 seconds
Requests processed: 5
Throughput: 9.74 requests/secondСпособность PagedAttention совместно использовать физическую память между различными сессиями открывает колоссальные возможности для оптимизации.
В сценариях сложного декодирования, таких как лучевой поиск или параллельное сэмплирование, родительская последовательность ветвится на множество дочерних. В vLLM все ветви ссылаются на единый физический кэш префикса, а копирование блоков происходит только в момент модификации.
Аналогичный подход применяется при обработке системных промптов: если множество пользователей взаимодействуют с агентом, имеющим объемный базовый контекст, этот контекст загружается в физические блоки памяти лишь единожды, сокращая потребление VRAM на порядок.
Важнейший компонент vLLM — это механизм диспетчеризации запросов. Традиционные системы ждали завершения обработки всего батча запросов перед загрузкой следующего. Технология Continuous Batching позволяет интегрировать новые запросы в освободившиеся слоты активного батча в процессе, не прерывая конвейер вычислений. При умеренной конкуренции пропускная способность масштабируется практически линейно до момента насыщения тензорных ядер или исчерпания VRAM.
Однако непрерывный батчинг порождает проблему дисбаланса. Интеграция нового запроса требует проведения ресурсоемкой фазы Prefill, которая блокирует выполнение фазы Decode для уже обрабатываемых в батче запросов, вызывая резкие скачки задержки генерации. Для устранения этого конфликта vLLM реализует функцию Chunked Prefill.
Алгоритм Chunked Prefill декомпозирует длинные входные промпты на блоки фиксированного размера, определяемого параметром max_num_batched_tokens. Планировщик отдает абсолютный приоритет фазе Decode, собирая все ожидающие операции декодирования в батч. Если в рамках установленного вычислительного бюджета остаются свободные слоты, планировщик заполняет их фрагментами фазы Prefill.
Такое смешивание операций, привязанных к вычислениям на фазе Prefill, и операций, привязанных к памяти на этапе Decode, в рамках одного матричного шага радикально улучшает утилизацию GPU и выравнивает задержку между токенами — метрику Inter-Token Latency, или ITL. Настройка параметра позволяет управлять компромиссом: увеличение бюджета ускоряет Time To First Token (TTFT), но может повысить ITL-задержку.
Для малых моделей на крупных GPU рекомендуется устанавливать лимит свыше 8 192 токенов.
В ситуациях экстремальной нагрузки, когда физическая память GPU полностью заполняется, vLLM применяет интеллектуальную политику прерывания запросов (preemption). Вместо того чтобы обрывать соединение или уничтожать вычисленный контекст, vLLM выгружает блоки KV-кэша менее приоритетных сессий в системную оперативную память хоста.
При освобождении места на GPU данные оперативно возвращаются по шине PCIe. Несмотря на то, что пропускная способность PCIe существенно уступает HBM, асинхронный характер переноса блоков позволяет скрывать задержки и доводить исполнение запроса до конца.
Дополнительно экосистема расширяется за счет внешних менеджеров хранения, таких как LMCache. Этот инструмент позволяет сохранять блоки KV-кэша не только в системной оперативной памяти, но и на локальных SSD-накопителях, создавая многоуровневую иерархию памяти.
Главное преимущество vLLM — это универсальность. Проект развивается как единая прослойка для работы с самыми разными аппаратными ускорителями.
Помимо экосистемы NVIDIA CUDA, vLLM из коробки поддерживает решения от других производителей: AMD ROCm, Intel XPU, Google TPU, AWS Neuron и Intel Gaudi. Кроме того, платформу можно запускать на процессорах архитектур x86 и ARM (включая Apple Silicon), а также на системах IBM Z. Благодаря этому vLLM отлично подходит для гетерогенных дата-центров, где код не должен зависеть от конкретного производителя чипов.
Однако кроссплатформенность нужна не всегда. Если в вашем распоряжении конкретный стек графических ускорителей и приоритетом является не всеядность бэкенда, а предельная скорость вычислений, стоит рассмотреть альтернативные решения.
Одним из таких узкоспециализированных инструментов является LMDeploy — комплексный проект, разработанный командами MMRazor и MMDeploy в рамках консорциума InternLM. Этот инструментарий изначально проектировался с фокусом на глубокое сжатие моделей и достижение максимальной производительности. Сердцем экосистемы стал C++/CUDA-движок TurboMind. В профильных сценариях он демонстрирует показатели пропускной способности, превосходящие базовый vLLM до 1,8 раза.
Движок TurboMind развивает идеи NVIDIA FasterTransformer. Фундаментом архитектуры служит модель Persistent Batch Inference. Процесс инференса рассматривается как непрерывно существующий батч с заранее аллоцированными слотами памяти. Запросы присоединяются к пулу при наличии свободных слотов и покидают его сразу после генерации EOS-токена, что позволяет слоту немедленно принять новый запрос. При наличии совпадений в кэше генерация начинается мгновенно, без повторного декодирования истории.
Механизм управления KV-кэшем в TurboMind работает как пул памяти с классической политикой вытеснения Least Recently Used (LRU). Менеджер выступает в роли кэша верхнего уровня. При переполнении пула он не уничтожает сессию безвозвратно, как это происходит в vLLM. Вместо этого вытесненная последовательность сжимается до своей самой компактной формы — последовательности идентификаторов токенов. Если клиент возобновляет работу с данной сессией, идентификаторы заново пропускаются через контекстный декодер для восстановления тензоров внимания. Для пользователя этот процесс прозрачен, что создает иллюзию обладания «бесконечной» видеопамятью для хранения сессий.
Для ускорения обработки длинных контекстов TurboMind применяет технологию Fast Context Decoding. Классические реализации внимания заменены на оптимизированные ядра Fused Multi-Head Attention (FMHA). Эта реализация позволяет оперировать ключами и запросами несовпадающей длины и использует систему косвенных указателей для адресации физически разрозненных фрагментов KV-кэша в пуле памяти. Дополнительно внедрены строгие барьеры синхронизации между потоками на стороне хоста для предотвращения зависаний NVIDIA Collective Communications Library (NCCL) при использовании тензорного параллелизма внутри одного процесса.
Как альтернатива механизму Chunked Prefill из vLLM, LMDeploy реализует алгоритм Dynamic Split and Fuse. Логика данного механизма заключается в строгом контроле размера вычислительного окна параметром max_prefill_iters.
Длинные запросы динамически фрагментируются, а короткие группируются таким образом, чтобы общий объем токенов в текущей матричной операции идеально соответствовал оптимальному размеру плитки для тензорных ядер. Это гарантирует непрерывную загрузку матрично-векторного вычислительного конвейера.
Асинхронные операции загрузки и сохранения предвыбирают последующие плитки в shared memory параллельно с выполнением умножений на тензорных ядрах, что позволяет приблизиться к теоретическому потолку HBM.
Главный плюс LMDeploy — глубоко оптимизированная система для работы с низкобитными форматами. В современном железе скорость генерации текста упирается именно в пропускную способность памяти. Поэтому сжатие весов модели и кэша дает кратный прирост производительности.
TurboMind предоставляет высокопроизводительные CUDA-ядра для вычислений со смешанной точностью: INT4 для весов и FP16 для активации. Профилирование показывает, что инференс в 4-битном формате с алгоритмом AWQ (Activation-aware Weight Quantization) выполняется до 2,4 раз быстрее, чем в нативном формате FP16.
На архитектурах от Turing до Hopper движок реализует целочисленное квантование 8-бит для весов и активаций. Модель проходит процедуру предварительного сглаживания весов, после чего стандартные слои линейных преобразований заменяются на модули QRSMNorm и QLinear, после чего модель сохраняется. Для архитектур уровня B200 уже реализована поддержка новейшего формата MXFP4.
Размер KV-кэша в многопользовательской среде с множеством параллельных запросов быстро превышает размер самой модели. LMDeploy позволяет осуществлять онлайн-квантование тензоров KV-кэша в форматы INT8 или INT4.
Как отмечают разработчики ядра, формат INT8 позволяет радикально сэкономить память при пренебрежимо малой потере точности генерации. Это даже заставило команду временно отказаться от разработки FP8 KV-кэша из-за низкого ROI в пользу целочисленного подхода. При этом стоит учитывать, что при квантовании кэша до INT4 падение точности становится заметным и требует более аккуратного тестирования.
Комбинация NVIDIA Triton Inference Server и библиотеки TensorRT-LLM — флагманское решение для извлечения физического максимума производительности из графических процессоров от NVIDIA. TensorRT-LLM представляет собой низкоуровневую библиотеку с открытым исходным кодом, которая транслирует модели из формата PyTorch в глубоко оптимизированные статические графы, содержащие специализированные ядра под конкретную архитектуру GPU.
Высокую производительность TRT-LLM обеспечивает симбиоз компилятора графов и кастомных CUDA-ядер, разрабатываемых инженерами NVIDIA
Компилятор trtllm-build анализирует граф модели и объединяет множество последовательных мелких операций в единое ядро. Это устраняет паразитные микросекундные задержки на запуск ядер и предотвращает циклы записи/чтения в глобальную память HBM, оставляя данные внутри быстрых регистров мультипроцессоров.
На архитектурах Hopper и Blackwell TensorRT-LLM автоматически активирует поддержку низкоточных вычислений с плавающей запятой. Использование FP8 не только сокращает потребление видеопамяти в два раза по сравнению с FP16, но и открывает доступ к инструкциям тензорных ядер удвоенной пропускной способности.
В итоге скорость генерации на том же самом чипе вырастает ровно в 2 раза. А новейшая архитектура Blackwell идет еще дальше — она бесшовно работает с форматом MXFP4, вообще не теряя в качестве ответов.
В целом такая поддержка есть и на vLLM. Чтобы оценить реальный прирост, мы провели небольшой тест производительности для модели Qwen3-Coder-Next-FP8. Мы сравнили ее работу на картах A100 (прошлое поколение Ampere) и H100 (актуальное поколение Hopper).
Для запуска модели использовалась следующая команда:
docker run --runtime nvidia --gpus all --ipc=host -v ~/.cache/huggingface:/root/.cache/huggingface -p 8000:8000 vllm/vllm-openai:latest --model Qwen/Qwen3-Coder-Next-FP8 --tensor-parallel-size 4 --max-model-len 8192 --quantization fp8 --kv-cache-dtype fp8 --gpu-memory-utilization 0.95 --enforce-eager --no-enable-log-requestsА это команда для тестирования:
vllm-bench --backend vllm --base-url http://127.0.0.1:8000 --model Qwen/Qwen3-Coder-Next-FP8 --dataset-name random --random-input-len 1024 --random-output-len 128 --num-prompts 1000 --max-concurrency 200По итогам были получены следующие метрики, для A100 и H100 соответственно:
================= Serving Benchmark Result =================
Successful requests: 1000
Failed requests: 0
Maximum request concurrency: 200
Benchmark duration (s): 385.78
Total input tokens: 1024000
Total generated tokens: 128000
Request throughput (req/s): 2.59
Output token throughput (tok/s): 331.80
Peak output token throughput (tok/s): 1600.00
Peak concurrent requests: 206.00
Total token throughput (tok/s): 2986.17
--------------------Time to First Token---------------------
Mean TTFT (ms): 9087.82
Median TTFT (ms): 2790.16
P99 TTFT (ms): 60469.75
P90 TTFT (ms): 33771.82
----------Time per Output Token (excl. 1st token)-----------
Mean TPOT (ms): 521.39
Median TPOT (ms): 568.33
P99 TPOT (ms): 588.63
P90 TPOT (ms): 583.92
--------------------Inter-token Latency---------------------
Mean ITL (ms): 521.39
Median ITL (ms): 616.69
P99 ITL (ms): 709.18
P90 ITL (ms): 700.27
---------------------End-to-end Latency---------------------
Mean E2EL (ms): 75304.26
Median E2EL (ms): 75805.70
P99 E2EL (ms): 130500.60
P90 E2EL (ms): 100268.97
============================================================
============ Steady-State Metrics =============
Concurrency threshold: >= 0.95 * 200 = 190
Window: 0.0s -> 369.4s (369.4s)
Observed peak concurrency: 200
Requests started in window: 811 / 1000 (81.1%)
Requests completed in window: 810
-----------------------------------------------
Request throughput (req/s): 2.19
Output token throughput (tok/s): 319.09
Input token throughput (tok/s): 2248.07
Total token throughput (tok/s): 2567.16
-----------------------------------------------
Mean TTFT (ms): 3590.42
Median TTFT (ms): 2609.35
P99 TTFT (ms): 58160.97
P90 TTFT (ms): 4902.15
-----------------------------------------------
Mean TPOT (ms): 522.37
Median TPOT (ms): 572.11
P90 TPOT (ms): 584.97
P99 TPOT (ms): 589.48
================================================================ Serving Benchmark Result =================
Successful requests: 1000
Failed requests: 0
Maximum request concurrency: 200
Benchmark duration (s): 57.82
Total input tokens: 1024000
Total generated tokens: 128000
Request throughput (req/s): 17.30
Output token throughput (tok/s): 2213.80
Peak output token throughput (tok/s): 6057.00
Peak concurrent requests: 248.00
Total token throughput (tok/s): 19924.23
--------------------Time to First Token---------------------
Mean TTFT (ms): 2520.42
Median TTFT (ms): 1377.56
P99 TTFT (ms): 10187.31
P90 TTFT (ms): 6838.14
----------Time per Output Token (excl. 1st token)-----------
Mean TPOT (ms): 68.88
Median TPOT (ms): 72.67
P99 TPOT (ms): 80.76
P90 TPOT (ms): 76.00
--------------------Inter-token Latency---------------------
Mean ITL (ms): 68.88
Median ITL (ms): 34.03
P99 ITL (ms): 281.04
P90 ITL (ms): 268.82
---------------------End-to-end Latency---------------------
Mean E2EL (ms): 11268.47
Median E2EL (ms): 10793.58
P99 E2EL (ms): 18365.80
P90 E2EL (ms): 15144.73
============================================================
============ Steady-State Metrics =============
Concurrency threshold: >= 0.95 * 200 = 190
Window: 0.0s -> 56.0s (56.0s)
Observed peak concurrency: 200
Requests started in window: 811 / 1000 (81.1%)
Requests completed in window: 810
-----------------------------------------------
Request throughput (req/s): 14.47
Output token throughput (tok/s): 2241.30
Input token throughput (tok/s): 14834.17
Total token throughput (tok/s): 17075.47
-----------------------------------------------
Mean TTFT (ms): 1545.47
Median TTFT (ms): 1111.28
P99 TTFT (ms): 9881.55
P90 TTFT (ms): 2433.74
-----------------------------------------------
Mean TPOT (ms): 69.02
Median TPOT (ms): 73.82
P90 TPOT (ms): 75.97
P99 TPOT (ms): 79.23
===============================================Из полученных данных можно сказать, что H100 показывает превосходство над A100 по всем ключевым метрикам практически на порядок.
Метрика | A100 | H100 | Улучшение на H100 |
Время выполнения | 385,78 с | 57,82 с | ~6,6x быстрее |
Request Throughput | 2,59 запр/с | 17,30 запр/с | ~6,6x выше |
Output Throughput | 331,80 ток/с | 2213,80 ток/с | ~6,6x выше |
Mean TTFT | 9087,82 мс | 2520,42 мс | ~3,6x быстрее |
Mean TPOT | 521,39 мс | 68,88 мс | ~7,5x быстрее |
По этому тесту можно увидеть нативную аппаратную поддержку FP8 архитектуры Hopper через Tensor Cores нового поколения. В свою очередь архитектура Ampere нативной поддержки вычислений FP8 не имеет, поэтому как vLLM, так и TRT-LLM приходится выполнять эмулирование операции, что приводит к огромным накладным расходам и убивает всю производительность.
Запускать FP8 квантизацию на архитектурах до Hopper в продакшене не рекомендуется.
TensorRT-LLM применяет похожую с vLLM архитектуру страничного кэширования. Размер блока кэша не является жестко зафиксированным: пользователь может настроить его на любую степень двойки вплоть до 128 токенов при сборке модели. Более крупные блоки повышают эффективность вычислений, но снижают гранулярность переиспользования кэша.
Интеллектуальное переиспользование префиксов в TRT-LLM реализовано через структуру радиксного дерева поиска. Система на лету определяет совпадения в истории промптов и связывает запросы.
Для устранения рисков безопасности в мультитенантных средах, где потенциально возможна атака кражи промпта, внедрена функция KV Cache Salting. Пользователь может передать параметр cache_salt, например, ID пользователя, и система разрешит переиспользование физических блоков только в том случае, если совпадают и токены префикса, и значение соли.
Более того, TRT-LLM реализует систему вытеснения блоков на основе приоритетов, позволяя приложению размечать важность различных частей промпта, а также предлагает API событий, транслирующий состояние кэша во внешние балансировщики нагрузки для маршрутизации запросов с учетом контекста. Более подробно это описано в статье Introducing New KV Cache Reuse Optimizations in NVIDIA TensorRT-LLM технического блога NVIDIA.
Triton Server служит мультифреймворковой оболочкой для управления скомпилированными графами TRT-LLM в промышленной среде. Типичный пайплайн LLM состоит из токенизатора, самой языковой модели и детокенизатора. Triton реализует сквозной конвейер двумя архитектурными способами.
Это декларативное описание DAG-графа зависимости через конфигурационные файлы config.pbtxt. Инференс ансамбля выполняется строго в высокопроизводительном слое C++ без копирования тензоров между этапами. Это обеспечивает максимальный показатель транзакций в секунду и почти 100% утилизацию GPU.
Недостаток ансамблей — невозможность внедрения сложной условной логики (циклов, if-else).
Business Logic Scripting (BLS) — это интеграция моделей с помощью Python-скриптов, которая решает проблему с более сложными потоками выполнения. Метод позволяет разработчику программировать ветвления и динамический роутинг. Однако платой за такую гибкость становится производительность: из-за накладных расходов межпроцессного взаимодействия и ограничений Python GIL метрика Tokens Per Second может падать до двух раз, а утилизация GPU снижаться до 50%.
Серверная часть TensorRT-LLM использует MPI для координации выполнения модели на нескольких графических процессорах и узлах. В настоящее время поддерживаются два различных режима для запуска модели на нескольких графических процессорах: режим лидера и режим оркестратора.
В режиме лидера серверная часть TensorRT-LLM запускает один серверный процесс Triton для каждого графического процессора.

В режиме «Orchestrator» серверная часть TensorRT-LLM запускает один серверный процесс Triton, который действует как оркестратор, и запускает один серверный процесс Triton для каждого графического процессора, который требуется каждой модели.

Второй метод может вызывать просадки в утилизации из-за избыточных затрат на коммуникацию между процессом-оркестратором и ведущим вычислительным узлом.
Отходя от конкретных инструментов, да и чтобы посмотреть на картину целиком, необходимо рассмотреть конкурирующие архитектурные концепции. В частности рассмотрим Text Generation Inference от Hugging Face и набирающий популярность подход дезагрегированного инференса.
Движок TGI v3 написан на языках Rust и Python. В основе третьей версии лежит агрессивное кэширование префиксов с оптимизированной структурой радиксного дерева и глубокий рефакторинг слияния микроядер. Инженерная структура кэша позволяет достичь времени поиска совпадений порядка пяти микросекунд. На длинных промптах TGI v3 обрабатывает ответ всего за две секунды, избегая повторного префилла, в то время как неоптимизированная под данный паттерн версия vLLM тратит 27,5 секунд.
Также по заявлениям документации, TGI удерживать в три раза больше токенов в памяти, приводя в пример сравнение в использовании 30 000 против 10 000 токенов LLama 3.1-8B в рамках одной видеокарты NVIDIA L4 24 ГБ по сравнению с vLLM.
Воспользуемся готовыми наработками в рамках свежей статьи vLLM vs TensorRT-LLM vs SGLang: H100 Benchmarks от марта 2026 года. Сравнение идет также с SGLang, однако в текущей статье, данные по результатам на этом движке будут опущены.
Тестирование проводилось на серверах bare-metal с видеокартой NVIDIA H100 80 ГБ. Анализировалась модель Llama-3.3-70B-Instruct в формате FP8.
Конкурентные запросы | vLLM (ток/с) | TensorRT-LLM Compiled (ток/с) |
1 запрос | 120 | 130 |
10 запросов | 650 | 710 |
50 запросов | 1,850 | 2,100 |
100 запросов | 2,400 | 2,780 |
Анализ данных подтверждает технологическое доминирование TensorRT-LLM на аппаратной базе NVIDIA. При минимальной нагрузке отрыв TRT-LLM от vLLM составляет скромные 8%. Однако по мере масштабирования конкуренции до 100 запросов разрыв увеличивается почти до 14%, достигая 2 780 токенов в секунду.
Concurrency | vLLM p50 | vLLM p95 | TRT-LLM p50 | TRT-LLM p95 |
1 req | 45 мс | 68 мс | 38 мс | 55 мс |
10 req | 120 мс | 195 мс | 105 мс | 170 мс |
50 req | 380 мс | 720 мс | 340 мс | 620 мс |
100 req | 740 мс | 1,450 мс | 680 мс | 1,280 мс |
Показатель отзывчивости сервиса (TTFT) также на стороне TRT-LLM. На 95-м перцентиле при 100 одновременных запросах TensorRT-LLM выдает первый токен на 170 миллисекунд быстрее, чем vLLM.
Тем не менее, эксплуатация TensorRT-LLM имеет очень серьезный инфраструктурный недостаток — время холодного старта. Из-за необходимости компиляции CUDA-графа, первая сборка движка TRT-LLM под конкретную модель на H100 занимает около 28 минут.
В то же время vLLM готов к обработке запросов спустя 62 секунды. Повторные запуски скомпилированного движка TRT-LLM занимают 90 секунд, однако для инфраструктур с частой ротацией моделей время начальной компиляции может стать неприемлемым препятствием. Именно данная проблема привела к тому, что мы в Selectel как раз и решили уйти от использования Triton.
Подытожив, можно сказать следующее:
используйте vLLM, если вам нужен самый быстрый путь к продакшену и гибкость при обновлении модели;
используйте TensorRT-LLM, если у вас есть одна модель для долгосрочного продакшену, а производительность имеет первостепенное значение.
Следующее тестирование, проведенное уже с использованием NVIDIA A100 80 ГБ и квантованной Q4/AWQ модели Llama 3 70B, выявляет сильные стороны LMDeploy.
В данном сценарии движок LMDeploy обеспечил наивысшую скорость генерации токенов, выдавая около 700 токенов в секунду при обслуживании 100 пользователей, сохраняя при этом самый низкий показатель TTFT.
TensorRT-LLM продемонстрировал аналогичную пиковую пропускную способность, но при нагрузке в 100 одновременных запросов его TTFT катастрофически деградировал до более чем шести секунд, что говорит о необходимости ручного тюнинга планировщика с подбором max_num_tokens для избежания перегрузки очереди префилла.
Движок vLLM показал стабильную пропускную способность на уровне 600–650 токенов в секунду. Его планировщик с алгоритмом PagedAttention и Chunked Prefill блестяще справился с контролем TTFT — показатель оставался стабильно низким на всем диапазоне нагрузок, защищая сервис от пиковых задержек.
Данные внутренних бенчмарков NVIDIA демонстрируют потенциал TensorRT-LLM на сверхплотных моделях.
При запуске массивной модели Llama 3.1 405B в формате FP8 на сборке Tensor Parallel = 8, видеокарты H200 на 141 ГБ демонстрируют пропускную способность в 4 715 токенов в секунду для коротких входных/выходных последовательностей – 128 входных / 2 048 выходных. В то время как для сверхдлинных контекстов – 20 000 входных / 2 000 выходных – производительность ожидаемо снижается до 509 токенов в секунду из-за насыщения шины памяти KV-кэшем.
Архитектура Blackwell переводит инференс на формат FP4. Запуск модели Llama 3.3 70B FP4 на платформе GB200 NVL72 продемонстрировало пропускную способность свыше 11 100 токенов в секунду на коротких запросах, что практически удваивает показатели предыдущего поколения за счет оптимизаций матричного умножения нового поколения.
Для модели Llama 4 Maverick FP8 на H200 задокументированы показатели свыше 27 500 токенов в секунду при длине 128/2048. Это подчеркивает тот факт, что аппаратные оптимизации формата данных способны принести больший эффект, чем изменения в логике планировщика программного обеспечения.
Развитие бэкендов для инференса LLM — классическая технологическая гонка: инновационные программные парадигмы по типу PagedAttention, Persistent Batching стремительно интегрируются конкурирующими проектами, в то время как производители кремния внедряют аппаратную поддержку низкоточных форматов FP8, FP4, а также новом NVFP4 на уровне архитектур самих GPU.
На основании проведенного анализа можно дать следующие рекомендации по выбору платформы для инференса моделей:
TensorRT-LLM и Triton Inference Server является ультимативным решением для развертывания устоявшихся моделей в высоконагруженных промышленных средах, эксплуатирующих графические процессоры NVIDIA архитектуры Hopper и Blackwell, где упор идет в достижение наилучших метрик моделей. За счет низкоуровневой компиляции графов, агрессивного слияния ядер и встроенной маршрутизации KV-кэша с учетом приоритетов, этот стек обеспечивает наивысшую пиковую пропускную способность и минимальное время до первого токена. Тем не менее, жесткая привязка к экосистеме NVIDIA и колоссальное время холодной сборки движка исключают его из сред, где сами модели могут или должны перезапускаться довольно часто.
vLLM остается самым популярным решением для быстрого инференса моделей с высокими метриками качества. Его ключевое преимущество — гибкость в настройке и выборе фирмы производителя, а также отличная оптимизация самого инференса. Алгоритмы PagedAttention, Chunked Prefill дают высокую отказоустойчивость и стабильность задержек даже в сценариях с множеством больших конкурентных запросов. Несмотря на 6–13% отставание по сырой пропускной способности от специализированного кода TRT-LLM, мгновенный запуск и простота интеграции благодаря OpenAI compatible API делают vLLM лучшим выбором по умолчанию для большинства команд.
LMDeploy с движком TurboMind выступает лучшим инструментом для развертывания компактных и средних моделей на ресурсоограниченном оборудовании. Интеграция механизма Dynamic Split and Fuse и превосходная реализация квантования W8A8 и AWQ позволяют TurboMind демонстрировать высочайшую плотность генерации токенов в секунду, превосходя конкурентов при использовании INT4 весов.
Дополнительно, для задач обработки сверхдлинных промптов и реализации паттернов RAG с огромными контекстами, целесообразно рассматривать движок TGI v3. В нем архитектура кэширования префиксов позволяет сокращать задержки вычисления истории с нескольких десятков секунд до микросекунд.