Больше интересного про инференс, AI-инфраструктуру и практику с LLM я публикую в Telegram-канале @fuckup_files.
Один из первых вопросов, который возникает у каждого, кто хочет запустить локальную LLM: сколько VRAM нужно для модели и влезет ли она в мою видеокарту? Ответ «посмотри на размер файла модели» — неправильный. Память под веса (weights) — это только часть истории, а на длинном контексте всё начинает съедать KV cache, про который новички обычно забывают.
В этой статье разберём по косточкам, из чего складывается потребление видеопамяти (VRAM) при инференсе LLM, дам формулы, по которым можно посчитать всё руками, и покажу на конкретных примерах (Llama 3.1 8B и 70B), почему «модель на 16 ГБ» может не запуститься на карте с 24 ГБ.
TL;DR
Потребление VRAM при инференсе складывается из трёх частей:
VRAM_total ≈ Веса + KV cache + Overhead
- Веса (weights):
число_параметров × байт_на_параметр. FP16 → 2 байта, FP8/INT8 → 1 байт, INT4 → ~0.5 байта. - KV cache: растёт линейно с длиной контекста и числом параллельных запросов. Вот где «прячется» память на длинном контексте.
- Overhead: активации (activations), CUDA-контекст, фрагментация, буферы фреймворка — обычно ещё 1–3+ ГБ.
| Модель | FP16 веса | + KV cache (контекст 8k, 1 запрос) | Реалистичный минимум VRAM |
|---|---|---|---|
| Llama 3.1 8B | ~16 ГБ | +1 ГБ | ~18–19 ГБ |
| Llama 3.1 70B | ~140 ГБ | +2.5 ГБ | не влезает в одну карту |
Если очень коротко: под веса прикидывайте «число параметров × байт на параметр», а сверху закладывайте запас под KV cache и overhead. Не влезает — квантуйте, режьте контекст или раскидывайте модель на несколько GPU.
Из чего складывается VRAM при инференсе
Когда модель работает на инференсе, в видеопамяти одновременно живут четыре вещи:
- Веса модели (model weights). Загружаются один раз и лежат в памяти постоянно. Самая предсказуемая часть.
- KV cache. Кэш ключей и значений (keys/values) механизма внимания. Хранит «прошлое» для каждого токена в контексте, чтобы не пересчитывать его на каждом шаге генерации. Подробно про сам механизм — в отдельной статье KV-cache в моделях transformers.
- Активации (activations). Промежуточные тензоры прямого прохода. Зависят от batch size и длины, но обычно меньше первых двух.
- Overhead фреймворка. CUDA-контекст (сотни МБ только на инициализацию), буферы communication-библиотек, фрагментация аллокатора, рабочие буферы. Это «налог», который легко недооценить.
Первые две части — главные. Дальше разберём каждую по формуле.
Память под веса: считаем по параметрам
Память под веса считается тривиально:
VRAM_weights ≈ N_params × bytes_per_param
bytes_per_param зависит от точности (precision), в которой загружена модель:
| Точность | Байт на параметр | Пример: 8B | Пример: 70B |
|---|---|---|---|
| FP32 | 4 | ~32 ГБ | ~280 ГБ |
| FP16 / BF16 | 2 | ~16 ГБ | ~140 ГБ |
| FP8 / INT8 | 1 | ~8 ГБ | ~70 ГБ |
| INT4 (AWQ/GPTQ/GGUF Q4) | ~0.5 | ~4–5 ГБ | ~35–40 ГБ |
Отсюда удобное эмпирическое правило: в FP16 модель занимает примерно вдвое больше гигабайт, чем у неё миллиардов параметров. 7–8B → ~16 ГБ, 70B → ~140 ГБ.
INT4 на практике даёт не ровно 0.5 байта на параметр: к квантованным весам добавляются scale-факторы и zero-points, плюс часть слоёв (например, эмбеддинги или нормализации) нередко остаётся в более высокой точности. Поэтому закладывайте реальные ~4.5–5 ГБ на 8B и ~38–42 ГБ на 70B. Что и как квантовать под своё железо — разбирал в статье Квантизация LLM в 2026: FP16, FP8, INT8, INT4, AWQ, GPTQ и GGUF.
KV cache: вот где прячется контекст
Если веса — это фиксированная стоимость, то KV cache — переменная, и именно она ломает наивные прикидки «модель 16 ГБ → карта 24 ГБ → всё ок».
Размер KV cache на один токен считается так:
bytes_per_token = 2 × num_layers × num_kv_heads × head_dim × bytes_per_element
Разберём множители:
- 2 — отдельно храним K (ключи) и V (значения).
- num_layers — число слоёв трансформера.
- num_kv_heads — число KV-голов. Это самый частый источник ошибки. В современных моделях используется grouped-query attention (GQA), где KV-голов меньше, чем attention-голов. Брать нужно именно
num_kv_heads, а не общее число голов внимания. - head_dim — размерность одной головы (обычно
hidden_size / num_attention_heads). - bytes_per_element — точность кэша: 2 байта для FP16/BF16, 1 байт для FP8/INT8-кэша.
Полный размер KV cache:
VRAM_kv = bytes_per_token × context_length × concurrent_sequences
То есть он растёт линейно и по длине контекста, и по числу параллельных запросов. Удвоили контекст — удвоили KV cache. Посадили 10 пользователей одновременно — умножили на 10.
Почему GQA так важен
Возьмём Llama 3.1 70B: 80 слоёв, 64 attention-головы, но всего 8 KV-голов (GQA), head_dim 128. Если по ошибке посчитать по 64 головам, получите KV cache в 8 раз больше реального — и сделаете вывод, что нужны 4 GPU вместо одной-двух.
Вот реальные числа на токен (FP16) для двух популярных моделей:
| Модель | Layers | KV heads | head_dim | KV cache на токен (FP16) |
|---|---|---|---|---|
| Llama 3.1 8B | 32 | 8 | 128 | ~128 КиБ (0.125 МБ) |
| Llama 3.1 70B | 80 | 8 | 128 | ~320 КиБ (0.3125 МБ) |
Как контекст съедает VRAM
Умножаем «на токен» на длину контекста (один запрос, FP16-кэш):
| Длина контекста | KV cache, 8B | KV cache, 70B |
|---|---|---|
| 4k | ~0.5 ГБ | ~1.25 ГБ |
| 8k | ~1 ГБ | ~2.5 ГБ |
| 32k | ~4 ГБ | ~10 ГБ |
| 128k | ~16 ГБ | ~40 ГБ |
Обратите внимание: у Llama 3.1 8B при контексте 128k KV cache (16 ГБ) сопоставим с самими весами (16 ГБ). А если поднять batch (несколько одновременных запросов), KV cache легко станет главным потребителем VRAM. Это не баг — это и есть причина, по которой длинный контекст «дорогой».
Активации, фрагментация и overhead фреймворка
Третья часть — то, что не сводится к красивой формуле, но что регулярно отъедает 1–3 ГБ и больше:
- CUDA-контекст. Сотни МБ просто на инициализацию рантайма на каждой карте.
- Активации (activations). Промежуточные тензоры. На инференсе они небольшие по сравнению с обучением, но при больших batch и длинном prefill заметны.
- Фрагментация аллокатора. Память выделяется блоками; часть теряется на «дыры». Именно с этим борется PagedAttention в vLLM, аллоцируя KV cache страницами.
- Рабочие буферы. CUDA graphs, временные тензоры, communication-буферы при multi-GPU.
На практике это значит: даже если по формуле получилось «впритык», в реальности нужен запас. Считайте теоретический минимум, прибавляйте overhead и не планируйте загрузку выше ~90% VRAM.
Как это видит vLLM
Здесь важная деталь, на которой спотыкаются многие. vLLM не «берёт сколько надо» — он заранее резервирует фиксированную долю всей видеопамяти под себя, параметром gpu_memory_utilization (по умолчанию 0.9, в свежих стабильных версиях — 0.92). Логика такая:
KV_cache_pool ≈ gpu_memory_utilization × total_VRAM − weights − activations
Сначала загружаются веса, а весь остаток из зарезервированной доли уходит в пул KV cache. Отсюда два практических следствия:
- Если
gpu_memory_utilization × total_VRAMменьше размера весов — vLLM просто не стартует (KV-пул отрицательный). Классический OOM ещё до первого запроса. - Чем больше весят веса, тем меньше остаётся под контекст и батч. Поэтому на тесной по памяти карте квантизация освобождает место не только «под модель», но и под более длинный контекст и больший batch.
# Тесно по памяти — можно осторожно поднять долю под KV cache
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--gpu-memory-utilization 0.92 \
--max-model-len 8192
Собираем всё вместе: формула и примеры
Итоговая прикидка:
VRAM_total ≈ N_params × bytes_per_param # веса
+ 2 × layers × kv_heads × head_dim × bytes # KV на токен
× context_length × concurrent_seqs # × контекст × запросы
+ overhead (≈ 1–3+ ГБ)
Пример 1: Llama 3.1 8B на одной RTX 3090 (24 ГБ)
FP16, контекст 8k, один запрос:
- Веса: ~16 ГБ
- KV cache (8k × 0.125 МБ): ~1 ГБ
- Overhead: ~1.5–2 ГБ
- Итого: ~18.5–19 ГБ → влезает в 24 ГБ, остаётся запас под небольшой batch.
Поднимаем контекст до 32k: KV cache становится ~4 ГБ, итого ~22 ГБ — впритык, на батч места почти нет. Решение — INT4: веса падают до ~5 ГБ, и под контекст с батчем освобождается куча места.
Пример 2: Llama 3.1 70B — почему одной карты мало
FP16 веса 70B — это ~140 ГБ. Это не влезет ни в RTX 3090/4090 (24 ГБ), ни даже в H100 (80 ГБ). Варианты:
- INT4: ~38–42 ГБ под веса. В одну 24 ГБ карту всё ещё не лезет. Две RTX 3090 (48 ГБ суммарно) — впритык по весам, на KV cache почти ничего не остаётся.
- FP8 на H100 (80 ГБ): ~70 ГБ веса + немного KV — помещается, но плотно.
- FP16 на 2× H100 (160 ГБ) через tensor parallel: комфортно.
Именно поэтому большие модели запускают на нескольких GPU — раскидывая веса и KV cache по картам через tensor parallel или pipeline parallel. Как это сделать на практике (несколько видеокарт, два сервера, Ray + vLLM) — в статье Распределённый запуск LLM на нескольких GPU с помощью Ray и vLLM.
Почему модель не влезает: быстрый чеклист
Поймали CUDA out of memory или vLLM не стартует — идём по списку от дешёвого к дорогому:
- Уменьшить
max_model_len. Самый быстрый рычаг: контекст 128k → 16k часто решает проблему, потому что режет максимальный размер KV cache. - Квантовать модель. FP16 → FP8 (÷2) или INT4 (÷4) по весам. Это и про «влезет», и про «останется место под KV cache».
- Снизить batch / число параллельных запросов. KV cache линеен по числу одновременных последовательностей.
- Проверить
gpu_memory_utilization. Если стоит 0.7 «на всякий случай» — поднимите до 0.9–0.92, чтобы отдать остаток под KV cache. Но помните: на этой же карте должно хватить и системе. - Включить FP8-кэш для KV. Кэш в 1 байт вместо 2 — это ×2 к ёмкости по контексту.
- Раскидать на несколько GPU. Tensor parallel делит и веса, и KV cache между картами. Когда модель физически не влезает в одну карту — это единственный путь.
Сколько VRAM нужно под популярные конфигурации (2026)
Грубый ориентир, FP16/типовая квантизация, умеренный контекст и batch:
| Хочу запустить | Минимум VRAM | Реалистичный сетап |
|---|---|---|
| 7–8B, FP16, контекст до 8k | ~20 ГБ | 1× RTX 3090/4090 (24 ГБ) |
| 7–8B, INT4, длинный контекст | ~10–12 ГБ | 1× карта 12–16 ГБ |
| 32–34B, INT4 | ~24–28 ГБ | 1× 24 ГБ впритык / 2× 24 ГБ |
| 70B, INT4 | ~44–48 ГБ | 2× RTX 3090 / 1× A100 80 ГБ |
| 70B, FP8/FP16 | ~80–160 ГБ | 1–2× H100/A100 80 ГБ |
Прежде чем мерить производительность, убедитесь, что выбрали адекватный контекст и batch под свою VRAM — про то, как корректно снимать TTFT, TPOT и эффективную ёмкость KV cache, писал в Как бенчмаркать локальную LLM в 2026.
Частые вопросы
Сколько VRAM нужно для 7B/8B модели? В FP16 — около 16 ГБ под веса плюс 1–4 ГБ под KV cache в зависимости от контекста, то есть реалистично 18–22 ГБ. В INT4 уложитесь в 10–12 ГБ.
Почему модель «на 16 ГБ» не влезает в карту с 24 ГБ? Потому что 16 ГБ — это только веса. Сверху идут KV cache (растёт с контекстом и батчем) и overhead фреймворка. На длинном контексте или с несколькими параллельными запросами суммарно легко выходит за 24 ГБ.
Как уменьшить размер KV cache?
Короче контекст (max_model_len), меньше параллельных запросов, FP8-кэш вместо FP16, плюс GQA/prefix caching снижают эффективное потребление. Подробнее про устройство кэша — в KV-cache в моделях transformers.
Можно ли запустить 70B на двух RTX 3090? Да, но только в квантизации (INT4 ~40 ГБ помещается в 48 ГБ суммарно) и с коротким контекстом — на KV cache остаётся мало места. Веса делятся между картами через tensor parallel; как настроить — в статье про распределённый инференс.
Что важнее для VRAM — размер модели или длина контекста? На коротком контексте доминируют веса. На длинном контексте (32k–128k) или при большом batch KV cache догоняет и перегоняет веса. Считать нужно обе части.
Заинтересовало? Больше практических разборов про LLM, инференс и AI-инфраструктуру — в моём Telegram-канале @fuckup_files.