Я прогнал топ выдачи Google по «llm vram calculator» — от самого навороченного до однокнопочных. Все ошибаются в одном и том же: ни один не моделирует, что движок резервирует почти всю память под пул заранее, а не раздаёт её под KV по мере запросов. Ответ «сколько запросов влезет» из-за этого всегда мимо.

Наивное большинство спотыкается ещё и о геометрию KV: на DeepSeek-V2-Lite обычная формула завышает память почти на порядок (в 7–11 раз). Разбираю обе ловушки и сверяю числа с живым vLLM.

Ошибка №1: движок забирает память заранее

Пишете две строки:

from vllm import LLM
llm = LLM("meta-llama/Meta-Llama-3-8B-Instruct")

Кажется, что движок положит веса, а остаток раздаст под KV по мере запросов. Так не делает ни один современный движок. vLLM при старте резервирует большой кусок VRAM под пул и пейджит KV внутрь него — флаг gpu_memory_utilization, дефолт 0.92. У SGLang то же зовётся mem_fraction_static (по умолчанию авто, около 0.9), у TensorRT-LLM — kv_cache_free_gpu_mem_fraction. Веса грузятся внутрь этого куска, KV живёт в остатке после весов и overhead; свободные ~8% VRAM под KV не пойдут никогда.

Ёмкость KV-пула поэтому считается так:

KV-пул = util · VRAM − веса − overhead

Если вы гоняете vLLM в проде, эту долю вы и так держите в голове: gpu_memory_utilization вы задаёте сами, а зачем пул выделяется одним куском, объясняет PagedAttention (arXiv:2309.06180).

Проверить, что пул посчитан верно, можно не арендуя GPU. vLLM при старте печатает строку # GPU blocks: N. Это и есть размер KV-пула. ridgepoint предсказывает то же число заранее:

ridgepoint fit llama-3-8b
ridgepoint fit llama-3-8b

llama-3-8b, 1× A100, ctx 8192: наивная формула без util обещает ~60 запросов; с поправкой на util — 54, замер vLLM — 55. Тот самый множитель, что вы и так держите в голове. С первой ошибкой всё.

Ошибка №2: геометрия KV, которую в уме не посчитать

KV на токен зависит от архитектуры внимания, и на этом спотыкается большинство калькуляторов (лучшие — вроде apxml — MLA всё же распознают). У обычной GQA-модели байты на токен считаются как 2·n_kv·head_dim·L·p. У DeepSeek с MLA внимание сжато в латент, и формула другая: (d_c + d_rope)·L·p, где d_c — размерность сжатого латента, d_rope — rope-часть.

Посчитаем DeepSeek-V2-Lite руками, из его config.json:

MLA (правильно):     (512 + 64)·27·2                = 31 104 Б/токен
наивная формула:     2·n_kv(16)·head_dim·27·2
   head_dim=128 (hidden/heads):  → 221 184 Б/токен  = завышение 7.1×
   head_dim=192 (qk_nope+rope):  → 331 776 Б/токен  = завышение 10.7×

Калькулятор, который трактует MLA как обычное внимание, подставляет 2·n_kv·head_dim. Смотря какой head_dim он возьмёт, KV завышается в 7–11 раз. Точный множитель зависит от его допущения, но порядок один, и он задокументирован в самой DeepSeek-V2 (arXiv:2405.04434): MLA сжимает KV в разы. И промах тут в обратную сторону от первой ошибки: вы решите, что модель требует почти на порядок больше карт, зря откажетесь её ставить или арендуете стойку вместо одной GPU.

С MoE та же ловушка: у DeepSeek-V2-Lite веса 16B (total), а активны 2B. Память платите за 16, скорость считаете по 2, перепутать легко.

Опираюсь я тут на измеренное: настоящие 31 104 Б/токен сняты с живого vLLM (40.79 ГиБ / 1 408 144 токенов), а (512+64)·27·2 сверьте сами. Инструмент берёт любой org/model с HuggingFace, читает конфиг и применяет формулу под конкретную архитектуру (GQA, MLA, MoE определяются сами):

ridgepoint fit DeepSeek-V2-Lite
ridgepoint fit DeepSeek-V2-Lite

Ради этого случая инструмент и писался.

Насколько числам можно верить

Наивная прикидка, ridgepoint и живой vLLM (замер на RunPod, vLLM 0.28, util 0.90):

1× A100-80GB, ctx 8192

наивно

ridgepoint

замер vLLM

llama-3-8b (GQA)

~60

54

55

DeepSeek-V2-Lite (MLA·MoE)

GQA-формула → 16–24

171

172

llama-3-70b AWQ

~15

12

13

Оговорю рамки. Память я сверял с логами vLLM и nvidia-smi на четырёх моделях трёх архитектур (GQA, MLA, MoE) на A100, плюс перенос на H100: по байтам расхождение 1–4% и всегда чуть ниже замера, то есть недооцениваю. Счётчики запросов в таблице округлены до целого, поэтому там разрыв может выглядеть крупнее (12 против 13 — это те же байты). Это n=4, не закон природы; полный predict-vs-measured лежит в репозитории (calibration/CALIBRATION.md). Побайтовый KV точен арифметически, если верно определена архитектура — ровно это определение и делает инструмент. Интервал overhead (1.5·1.8·2.2 в выводе — активации плюс CUDA-графы) уже эмпирика, её я и калибровал, отсюда полоса low/best/high. Дефолт vLLM с тех пор уехал с 0.90 на 0.92, значит реальный пул чуть больше моих чисел — я и здесь недооцениваю.

Где инструмент не помогает

Скорость. TTFT и утилизацию компьюта (MFU, model FLOPs utilization) замерить чисто не вышло: удалённый бенчмарк мешает префилл с сетью, поэтому в выводе они помечены ~MFU literature — цифры из литературы, сам я их не мерил. Пропускную способность памяти при декоде (MBU, memory-bandwidth utilization ≈ 0.62) померил, этой цифре верю. Throughput под смешанной нагрузкой (--max-num-seqs, разные длины) v0 не считает, это следующая версия. И числа пока калиброваны на vLLM: SGLang с TensorRT-LLM пейджат KV в такой же пул, но коэффициенты под них я ещё не мерил.

Что показал прогон

Взял то, что видит обычный человек в выдаче — и pre-grab движка не моделирует никто, даже лучший из них.

калькулятор

движок (pre-grab)

MLA

overhead

«сколько влезет»

apxml (топ-1)

concurrency вводишь ты

NyxKrage (513 ♥)

batch на вход

smcleod

❌ (один поток)

ridgepoint

✅ из пула движка

apxml реально силён: распознаёт MLA, считает framework overhead, умеет KV-квант. Но Concurrent Users — это ползунок на вход, а память он складывает как «веса + активации + KV + overhead < VRAM». Что vLLM заберёт gpu_memory_utilization заранее и сколько запросов влезет в оставшийся пул — не моделирует.

apxml: MLA и overhead есть, но concurrency — на вход, а pre-grab движка не учитывается
apxml: MLA и overhead есть, но concurrency — на вход, а pre-grab движка не учитывается

smcleod и NyxKrage проще: Model + KV = Total, без overhead и без MLA (значит, на DeepSeek — те самые 7–11×). Проверьте сами за минуту по ссылкам.

smcleod: «Model + KV = Total» — ни overhead, ни движка
smcleod: «Model + KV = Total» — ни overhead, ни движка

И это не про «старьё»: механизм публичен с PagedAttention (2023), а тулы 2026 года его всё равно не берут.

Попробовать на своём. GPU не нужен

pip install ridgepoint
ridgepoint fit deepseek-ai/DeepSeek-V2-Lite --gpu h100-80gb:1 --ctx 8192

Считает локально, на ноутбуке: тянет с HuggingFace только config.json и индекс safetensors (пара КБ), больше ничего никуда не уходит (python/ridgepoint/hf.py). Жечь GPU-часы не нужно.

Дальше подставляйте что нужно. Любой HF-репозиторий подтянет конфиг сам. Карта задаётся флагом --gpu a100-80gb:1 или h100-80gb:2, а ridgepoint devices найдёт локальные. Движок — --engine vllm|sglang|llamacpp; SGLang уже работает (пул тот же pre-grab), но помечен uncalibrated — коэффициенты под него на железе я ещё не мерил. Если удобнее из кода — есть библиотечный вызов ridgepoint.fit("llama-3-70b", "a100-80gb", count=2).

Ядро на Rust считает только числа и в сеть не ходит. HF-адаптер живёт снаружи, на Python.

Что дальше

v0 узкий: память и ёмкость, пока два движка (vLLM и llama.cpp). Но внутри уже не наивно — веса и KV-кэш квантуются раздельно (--kv-cache-dtype fp8 вдвое увеличивает KV-ёмкость пула; кто ставит fp4-веса при fp16-кеше, обычно этого не осознаёт), а MLA/MoE/GQA определяются сами. В очереди, по одному измеренному куску за релиз:

  • Докалибровать движки на железе. SGLang уже запускается (--engine sglang), но пока uncalibrated — с него и начну (сам его предпочитаю), дальше TensorRT-LLM и TGI. Пул у всех один и тот же pre-grab, отличаются только коэффициенты.

  • Полный шкаф железа и квантов: Blackwell, H200, MI300X, NVLink. Заодно покажу, как FP8/FP4 двигают обе оси roofline — байты весов вниз и пиковые FLOPs вверх.

  • Скорость замером на поде, а не из литературы. Сейчас в выводе там заглушка ~MFU literature, вы её видели.

  • Несколько GPU: TP/PP/EP. В замерах уже вижу, что активации шардятся, а CUDA-графы на карту нет.

  • Спек-декодинг. Он переводит decode из memory-bound в compute-bound, тащит тебя к тому самому ridge point, в честь которого инструмент назван.

  • Mamba/SSM, sliding-window, гибриды — где KV перестаёт расти линейно по контексту.

  • Оффлоуд KV в CPU/NVMe и деньги: $/1М токенов против облачного API.

Код лежит открыто: github.com/Isk4R1oT/ridgepoint. Буду рад звезде, а ещё больше — issue с моделью, на которой мои числа разошлись с вашим vLLM.


Вопрос к тем, кто гоняет vLLM в проде: сверьте # GPU blocks из своего лога старта с тем, что предскажет ridgepoint fit на вашей модели. Совпало? И ловили ли OOM там, где по расчёту всё влезало?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Ловили, что по расчёту модель влезала, а на деле — нет?
20%Да, ловил OOM1
40%Да, наоборот — зря не поставил, думал не влезет2
20%Нет, всё сходилось1
20%Не считаю заранее, гоняю по факту1
Проголосовали 5 пользователей. Воздержались 2 пользователя.