Больше интересного про инференс, AI-инфраструктуру и практику с LLM я публикую в Telegram-канале @fuckup_files.
Движок инференса вы выбираете не первым. Сначала идут железо, форма нагрузки и модель обслуживания. Движок просто следует из этих решений.
Это самый полезный способ думать о движках инференса для LLM.
Inference Engines
Они решают разные задачи и живут на разных уровнях:
- портативный запуск на чём угодно;
- потребительские сборки на CUDA;
- рабочие процессы на унифицированной памяти Apple;
- квантизованный инференс;
- промышленное обслуживание;
- распределённая оркестрация;
- датацентровое исполнение с вендорной оптимизацией.
Удобная аналогия: движок инференса это не просто "модель". Он одновременно и регулировщик трафика, и менеджер памяти, и диспетчер ядер, и планировщик, и учёт кэша, и API-фасад, а иногда и целый фреймворк развёртывания.
Хороший движок подбирают под иерархию памяти, межсоединения, формат квантизации, целевые задержку и пропускную способность, архитектуру модели и зрелость эксплуатации.
Шпаргалка по выбору
- ноутбук, периферия, нестандартное железо -> llama.cpp
- рабочие процессы на Mac -> MLX / MLX-LM
- локальный инференс на одной RTX -> ExLlamaV2
- 2–4 и больше NVIDIA / CUDA -> vLLM, SGLang
- промышленное обслуживание общего назначения -> vLLM, SGLang
- длинный контекст, MoE, маршрутизация -> SGLang
- максимум производительности на NVIDIA -> TensorRT-LLM
- оркестрация кластера -> NVIDIA Dynamo
Дальше объясню, почему именно так.
Что движок инференса делает на самом деле
Движок загружает веса, токенизирует вход, гоняет прямой проход, сэмплирует токены, ведёт KV-кэш и стримит результат. Серьёзные движки сверх этого занимаются батчингом, планированием, кэшированием префиксов, квантизацией, параллелизмом, API, метриками и распределённым исполнением.
Нагрузка делится на две фазы.
Prefill читает промпт и строит исходный KV-кэш. Это про вычисления.
Decode генерирует по одному токену, постоянно обращаясь к весам и KV-кэшу. Это про пропускную способность памяти. Скорость декодирования упирается именно в неё, а не в пиковые FLOPS.
Из этого различия следует почти всё:
- короткий промпт, длинный ответ: доминирует decode, важны пропускная способность памяти и батчинг;
- длинный промпт, короткий ответ: доминирует prefill, важны ядра attention и chunked prefill;
- много пользователей: важно качество планировщика, а именно непрерывный батчинг, кэширование страниц и справедливость между пользователями;
- длинный контекст: доминирует KV-кэш, нужны paged attention, квантизация KV и выгрузка на диск;
- MoE: доминирует маршрутизация экспертов, нужны параллелизм экспертов, межсоединения и групповые GEMM;
- много нод: доминируют межсоединения, а именно NVLink, RDMA, конвейерный параллелизм и дезагрегация.
PagedAttention решил фрагментацию KV-кэша. FlashAttention снял лишний трафик по HBM за счёт IO-осознанного тайлинга. Спекулятивное декодирование набрасывает дешёвые токены и проверяет их параллельно. Лейтмотив всегда один и тот же: производительность инференса складывается из перемещения данных и планирования.
Реальные узкие места
1. Пропускная способность памяти, а не объём VRAM. Объём решает, влезет ли модель. Пропускная способность решает, насколько быстро она будет декодировать. У Apple M3 Ultra унифицированная память выдаёт до 819 ГБ/с, у NVIDIA H100 SXM по GPU-памяти до 3,35 ТБ/с. Унифицированная память позволяет загрузить модель, которая не поместится в потребительский VRAM. HBM позволяет отдавать её быстрее, когда модель в неё помещается. Объём не равен скорости. Ёмкость не равна пропускной способности.
2. Рост KV-кэша. KV-кэш растёт вместе с размером батча и длиной контекста. На длинном контексте память может закончиться раньше, чем перестанут помещаться веса. PagedAttention режет кэш на блоки, поднимает утилизацию и позволяет держать батчи побольше.
3. Межсоединения. Как только модель уходит за пределы одного GPU, начинается плата за коммуникацию. Тензорный параллелизм требует частых all-reduce. Конвейерный общается на границах стадий. Экспертный гоняет all-to-all для MoE. В документации vLLM прямо сказано: без NVLink конвейерный параллелизм может оказаться быстрее тензорного.
4. Качество планировщика. Хороший планировщик решает, какие запросы пойдут в один батч, как prefill и decode делят ускоритель, не блокируют ли длинные промпты короткие декодирования, как избежать голодания. Между «поддержкой батчинга» и «продакшен-планировщиком» огромная разница.
5. Накладные расходы рантайма. CUDA graphs, слияние ядер, сэмплирование, токенизатор, HTTP, переключение LoRA, структурированное декодирование. Каждый кусочек стоит времени. На большой нагрузке досадные «всего лишь 2%» складываются и требуют внимания.
Семейства движков
Четыре крупные категории.
Портативные локальные рантаймы: llama.cpp, MLC LLM, ONNX Runtime GenAI, OpenVINO, инструменты из мира Ollama. Их задача: "запустить вот это здесь".
Рантаймы Apple на унифицированной памяти: MLX и MLX-LM. Их задача: "толково использовать общую память и стек Apple".
Движки для квантизации на потребительских CUDA: ExLlamaV2 и ExLlamaV3. Их задача: "выжать из моего 3090/4090/5090 всё, что можно, на низкобитных весах". Ограничения в выборе моделей.
Движки для промышленного обслуживания: vLLM, SGLang, TensorRT-LLM, TGI, LMDeploy. Их задача: параллельные пользователи, KV-кэш, батчинг, параллелизм, наблюдаемость и цена за токен.
Отдельно стоят оркестрационные слои вроде Dynamo: они живут над движками и координируют парки серверов, p/d disaggregation, маршрутизацию и автомасштабирование.
llama.cpp: король портативности
llama.cpp отвечает на случай, когда железо нестандартное, ограниченное, офлайновое, упирается в CPU, ориентировано на периферию или вообще не похоже на аккуратный датацентровый узел NVIDIA.
Он умеет: Apple Silicon через ARM NEON, Accelerate и Metal; x86 через AVX/AVX2/AVX512/AMX; RISC-V; низкобитную квантизацию; CUDA; AMD через HIP; MUSA; Vulkan; SYCL; гибридную выгрузку CPU+GPU. Из-за такого набора llama.cpp и закрепился в нише «просто запусти».
HTTP-сервер здесь куда мощнее, чем подсказывает фраза «локальный раннер». llama-server отдаёт OpenAI-совместимые маршруты, совместим с Anthropic Messages API, умеет переранжирование, непрерывный батчинг, мультимодальность, ограничения JSON-схемы, вызов функций, спекулятивное декодирование и веб-интерфейс.
Важное ограничение: llama.cpp не предназначен для серьёзного многоузлового продакшена. RPC-бэкенд в документации прямо помечен как proof-of-concept: хрупкий и небезопасный.
Вердикт: берите llama.cpp, когда портативность, офлайн, GGUF или гибридная выгрузка важнее обслуживания парка серверов.
> На нескольких GPU использовать не стоит.
MLX и MLX-LM: оружие Apple Silicon
MLX это фреймворк работы с массивами для Apple Silicon, а MLX-LM надстроенный над ним пакет для LLM. По сути, Mac-ориентированный ML-стек.
Главный аппаратный факт состоит в унифицированной памяти. У Apple Silicon CPU и GPU напрямую обращаются к одному пулу. Массивы MLX живут в этой памяти, а устройство (CPU или GPU) вы выбираете в момент операции, не таская массивы между раздельными адресными пространствами.
Это меняет привычные компромиссы локального инференса. На системе с дискретным GPU вопрос звучит как «влезет в VRAM?». На M-серии с большой унифицированной памятью он другой: «влезет в память и сумеет ли подсистема памяти кормить GPU достаточно быстро?». Крупные квантизованные модели вполне помещаются на машинах, где аналогичный инференс на потребительском GPU с 24 ГБ был бы невозможен.
Зато медленнее.
MLX-LM добавляет интеграцию с Hugging Face Hub, квантизацию, LoRA и полный файнтюн, распределённый инференс и большой каталог моделей MLX Community. MLX уже не только про Mac: есть сборки под CUDA и CPU-only для Linux. Распределённые коммуникации идут через MPI, Ring поверх TCP, JACCL для RDMA через Thunderbolt и NCCL для CUDA.
Сам MLX-LM-сервер в документации честно предупреждает, что для продакшена не годится, потому что реализованы только базовые проверки безопасности.
Вердикт: MLX подходит для Mac-ориентированных ML- и LLM-сценариев. Под высокую публичную нагрузку берите нормальный серверный стек.
ExLlamaV2 и V3: потребительский CUDA, настроенный и быстрый
ExLlamaV2 это движок локальной квантизации под CUDA для тех, кто хочет заставить потребительский NVIDIA работать выше своего класса. Он поддерживает paged attention, динамический батчинг, кэширование промптов, дедупликацию KV-кэша, пакетную генерацию, стриминг и спекулятивное декодирование. Ключевое слово здесь «локальный»: он разгоняет квантизованные модели на современных CUDA-картах, в первую очередь потребительских.
Лучший сценарий: одиночная RTX 3090/4090/5090, локальный ассистент для кода, локальный чат, модели в формате EXL2, использование на профессиональных рабочих станциях.
ExLlamaV3 двигает ту же философию в сторону многокарточного инференса и MoE. Добавлены формат EXL3 на базе QTIP, гибкий тензорный и экспертный параллелизм для потребительского железа, OpenAI-совместимый сервер через TabbyAPI, непрерывный динамический батчинг и мультимодальность.
V3 интересен, если у вас 2–4+ потребительских NVIDIA или нужен локальный MoE. С оговорками: часть моделей не поддерживает тензорный или экспертный параллелизм в ExLlamaV3.
Вердикт: ExLlamaV2 остаётся движком энтузиаста для локального CUDA. ExLlamaV3 это передний край локальных сборок на 2–4 GPU. За лучшие фичи приходится платить шероховатостями. Не каждая модель на момент выхода имеет совместимость с ExLlama - необходимо проверять.
vLLM: opensource-сервер по умолчанию для продакшена
vLLM это первый движок, который стоит примерить на серьёзное обслуживание opensource-моделей.
В наличии: управление KV-памятью на базе PagedAttention, непрерывный батчинг, chunked prefill, кэширование префиксов, CUDA/HIP graphs, широкий набор квантизаций (FP8, MXFP8/MXFP4, NVFP4, INT8, INT4, GPTQ, AWQ, GGUF), оптимизированные ядра attention и GEMM/MoE, спекулятивное декодирование, torch.compile, дезагрегированные prefill/decode/encode.
И гибкость: тензорный / конвейерный / data / expert / контекстный параллелизм, стриминг, структурированный вывод, вызов инструментов, OpenAI- и Anthropic-совместимые API, gRPC, multi-LoRA, поддержка NVIDIA, AMD, CPU x86/ARM/PowerPC, плюс плагины для TPU, Gaudi, Ascend, Apple Silicon и других.
Документация vLLM прямо говорит, что многоузловые развёртывания обычно живут на Ray, а без NVLink конвейерный параллелизм может обогнать тензорный. Ловушка в том, чтобы считать, будто vLLM избавляет от системного мышления. Батчинг, длину контекста, утилизацию GPU-памяти, схему параллелизма и маршрутизацию всё равно настраивать руками. vLLM даёт очень хороший движок, но за инженерию по-прежнему отвечаете вы.
Вердикт: если на столе задача "нам нужно обслуживать opensource-модели в продакшене", vLLM становится точкой отсчёта по умолчанию.
SGLang: системный родственник vLLM
SGLang выручает, когда нагрузка неудобная: структурированный вывод, длинный контекст, MoE, дезагрегация, маршрутизация.
В наборе: кэширование префиксов RadixAttention, дезагрегация prefill-decode, спекулятивное декодирование, непрерывный батчинг, paged attention, тензорный / конвейерный / экспертный / data параллелизм, структурированный вывод, chunked prefill, multi-LoRA-батчинг. Железо: NVIDIA, AMD, Intel Xeon, Google TPU, Ascend NPU и другие.
Главное отличие SGLang в архитектуре обслуживания. Дезагрегация prefill-decode уносит вычислительно тяжёлый prefill и memory-bound decode на специализированные инстансы и передаёт KV-кэш между ними. Длинные prefill-батчи больше не блокируют декодирование, а задержка токенов не скачет.
Вердикт: SGLang создан для команд, у которых вопрос стоит уже не "запустим ли мы модель", а "получится ли выдержать всю нагрузку без ущерба скорости, памяти и стоимости".
TensorRT-LLM: максимум производительности на NVIDIA
TensorRT-LLM это стек максимальной производительности от NVIDIA. Оптимизированный, узкоспециализированный, мощный, без претензий на портативность.
В наличии Python API для сборки движков TensorRT с последними оптимизациями, плюс Python- и C++-рантаймы. Внутри есть собственные ядра под attention, GEMM и MoE; дезагрегация prefill-decode, широкий параллелизм экспертов, спекулятивное декодирование; высокоуровневый Python API, интегрированный с NVIDIA Dynamo и Triton Inference Server.
GPU серии B200 умеют грузить веса FP4 со специализированными ядрами. H100 и новее поддерживают FP8, который может удвоить производительность и вдвое уронить расход памяти по сравнению с 16-битными весами, причём при минимальной потере точности.
Где блистает: парки серверов класса H100/H200/B200/GB200/GB300, датацентры только на NVIDIA, развёртывание FP8/FP4, многоузловое обслуживание, MoE в масштабе. Где неудобен: портативность на AMD / Apple / Intel, быстро меняющиеся экспериментальные модели, мелкие локальные сборки, команды, которым нужно «лишь бы работало везде».
Вердикт: если вы прибиты гвоздями к NVIDIA и упёрты в абсолютную производительность, TensorRT-LLM достоин места на стенде. Вы меняете портативность на скорость: точечная специализация, но меньше функций.
Остальные игроки
TGI это продакшен-сервер от Hugging Face: трассировка, метрики, тензорный параллелизм, непрерывный батчинг. Стоит брать, когда важна интеграция с HF и простота.
MLC LLM это компиляторный движок универсального развёртывания с OpenAI-совместимыми API через REST, Python, JavaScript, iOS и Android. Идеален, чтобы «привинтить LLM везде», особенно в браузере, на мобилке и в нативных приложениях.
ONNX Runtime GenAI реализует полный генеративный цикл поверх ONNX Runtime и лежит в основе Foundry Local, Windows ML и VS Code AI Toolkit. Поддерживает CPU, CUDA, DirectML, TensorRT-RTX, OpenVINO, QNN, WebGPU и AMD GPU. Хорош для встраивания в приложения и ONNX-пайплайнов.
OpenVINO GenAI это Intel-оптимизированное решение для Xeon CPU, Arc GPU, Core Ultra и NPU. Даёт OpenAI-совместимое обслуживание с непрерывным батчингом и paged attention. Лучший выбор под Intel-железо.
LMDeploy это CUDA-ориентированный инструментарий с TurboMind (под производительность) и PyTorch (под доступность). Любопытен прежде всего CUDA-пользователям, которые хотят альтернативу vLLM/SGLang/TensorRT-LLM.
NVIDIA Dynamo это распределённый оркестрационный слой над vLLM, SGLang и TensorRT-LLM. Поддерживает дезагрегацию, умную маршрутизацию и многоуровневое кэширование KV. Идёт в дело, когда обслуживание одним движком уже не вытягивает.
Примечание: Ollama использовать НЕ СЛЕДУЕТ.
Рецепты под конкретное железо
Сервер только на CPU: первым делом llama.cpp. OpenVINO подойдёт под Intel Xeon, ONNX Runtime GenAI под встраивание и ONNX-пайплайны.
MacBook / Mac Studio: MLX / MLX-LM под Mac-нативные сценарии. llama.cpp под GGUF и портативность.
Одна RTX 3090 / 4090 / 5090: ExLlamaV2 для локального инференса на EXL2. llama.cpp для GGUF и портативности. vLLM, если вы реально обслуживаете нескольких пользователей.
Сборка из 2–4 потребительских RTX: ExLlamaV3 под многокарточный квантизованный инференс или MoE. vLLM, если важно поведение под нагрузкой. SGLang при тестировании маршрутизации и сценариев с длинным контекстом.
Узел 8×H100 / H200: начните с vLLM или SGLang. Подключайте TensorRT-LLM, если вы строго на NVIDIA и производительность оправдывает тюнинг. Dynamo подключайте, когда становится нужна многоузловая оркестрация.
Инфраструктура B200 / GB200 / GB300: бенчмарки TensorRT-LLM, SGLang и vLLM в очной ставке. Сверху добавьте Dynamo для оркестрации парка, KV-aware маршрутизации и автомасштабирования.
AMD MI300 / MI325 / MI350 / MI355: vLLM или SGLang на ROCm. Не предполагайте, что бенчмарки с NVIDIA напрямую переносятся.
Intel Xeon / Core Ultra / Arc: OpenVINO GenAI или OpenVINO Model Server. ONNX Runtime GenAI при встраивании в приложения.
Браузер, мобилки, нативные приложения, Windows-native: MLC LLM / WebLLM либо ONNX Runtime GenAI.
Бенчмаркинг: что мерить
Плохой бенчмарк: "получил 180 т/с".
Хороший бенчмарк включает:
Модель: какую именно, архитектура, количество параметров, активные параметры MoE.
Веса: dtype, формат квантизации, размер группы, калибровка.
Движок: версия, коммит, бэкенд, флаги.
Железо: SKU GPU, объём памяти, пропускная способность, межсоединения, CPU, RAM.
Нагрузка: распределения длин входа/выхода, конкурентность, стриминг, общие префиксы, структурированный вывод.
Метрики: TTFT, TPOT, end-to-end задержка, p50/p95/p99, токены в секунду, запросы в секунду, расход GPU-памяти, hit rate KV-кэша, пропускная способность prefill, пропускная способность decode, цена за 1М токенов.
Правила бенчмаркинга
1. Никогда не сравнивайте движки только по tok/s одиночного пользователя.
2. Гоняйте реальное распределение ваших промптов и ответов.
3. Гоняйте под реалистичной конкурентностью.
4. Разделяйте prefill и decode.
5. Смотрите p95 и p99, не только средние.
6. Меряйте запас памяти на целевой длине контекста.
7. Тестируйте повторное использование кэша, если у приложения повторяются префиксы.
8. Структурированный вывод гоняйте отдельно: грамматика добавляет накладные расходы.
9. LoRA и multi-LoRA тоже отдельно.
10. Перепрогоняйте после апдейтов драйверов, CUDA, ROCm, модели или движка.
Типичные ошибки
Выбор по объёму VRAM в одиночку. VRAM решает, влезет ли модель. Скорость задают пропускная способность и планировщик. Машина с большой унифицированной памятью вмещает огромные модели, но H100 декодирует быстрее, когда модель помещается, потому что у HBM в разы большая пропускная способность.
Тензорный параллелизм при слабых межсоединениях. Без NVLink или NVSwitch попробуйте конвейерный, об этом прямо сказано в документации vLLM для конфигураций вроде L40S.
Игнорирование KV-кэша. Длинный контекст и конкурентность превращают KV-кэш в ограничивающий фактор. PagedAttention, кэширование префиксов, квантизация KV и дезагрегация в масштабе перестают быть опцией и становятся обязательной программой.
Локальный движок в роли продакшен-сервера. Сервер llama.cpp вполне жизнеспособен. Сервер MLX-LM удобный. Ollama приятный, но использовать его не следует.
Только вот продакшен означает безопасность, наблюдаемость, обратное давление, маршрутизацию, автомасштабирование и поведение под SLA. Сам MLX-LM прямо предупреждает: его сервер для продакшена не рекомендуется.
Допущение, что форматы квантизации взаимозаменяемы. Форматы GGUF, EXL2, EXL3, AWQ, GPTQ, FP8, FP4, MLX и ONNX не взаимозаменяемы. Правильным будет тот, под который у вашего движка есть оптимизированные ядра.
Игнорирование архитектуры модели. Плотные, MoE, гибридный attention, мультимодальные и long-context варианты грузят разные части движка. Широкая поддержка не значит, что каждая оптимизация работает одинаково.
Доверие графикам бенчмарков без оглядки на форму нагрузки. График для Llama 3.1 8B на вход 1K / выход 128 говорит немногое об агенте для кодинга с контекстом 80K на Qwen 3.6 27B / Gemma 4 26B-A4B или о RAG-сервисе с 500 параллельных пользователей.
Итоговая карта рекомендаций
Локальный пользователь AI: LM Studio или Harbor для удобства. llama.cpp для контроля. MLX на Mac. ExLlamaV2/V3 для локальной производительности на CUDA.
Локальная разработка агентов: подойдёт почти всё, но с поправкой на то, что выбирает большинство: llama.cpp для портативности; MLX, если у пользователей Apple Silicon; vLLM, когда хочется локально воспроизвести продакшен-обслуживание.
Обслуживание внутренней команды: начните с vLLM. Переезжайте на SGLang, если важны структурированный вывод, длинный контекст, multi-LoRA, MoE или маршрутизация.
Обслуживание клиентов в масштабе: гоняйте vLLM, SGLang и TensorRT-LLM. Если на первом плане маршрутизация и дезагрегация, присмотритесь к SGLang и Dynamo.
Датацентр NVIDIA: TensorRT-LLM за максимум производительности. vLLM за гибкость. SGLang за сложное обслуживание. Dynamo за оркестрацию парка.
Apple Silicon: MLX для нативной разработки. llama.cpp для GGUF. Унифицированная память даёт суперсилу по ёмкости с компромиссами по пропускной способности; это не HBM.
Периферия, приложения, браузер, Windows-native: llama.cpp, MLC LLM, ONNX Runtime GenAI или OpenVINO, в зависимости от стека.
Финальный принцип
У движков инференса есть последствия.
Чтобы выбрать движок, ответьте на эти вопросы:
1. Какое у меня железо на самом деле?
2. Помещается ли модель в быструю память или только в системную / унифицированную?
3. Что является узким местом: decode или prefill?
4. Какие длина контекста и конкурентность реально важны?
5. Похожи ли промпты друг на друга достаточно, чтобы кэшировать префиксы?
6. Модель плотная, MoE, мультимодальная или гибридная?
7. Что мне нужно: локальное удобство, промышленное обслуживание или оркестрация парка?
8. Под какой формат квантизации в моём целевом движке есть оптимизированные ядра?
9. Какие у меня межсоединения: PCIe, NVLink, NVSwitch, Ethernet, RDMA или Thunderbolt?
10. Что я оптимизирую: задержку, пропускную способность, стоимость, приватность, портативность или скорость разработки?
Движок придёт следом за ответами.
Оригинал статьи: https://x.com/TheAhmadOsman/status/2057183854444843202