Сайзинг RAM и vCPU для локальной языковой модели начинается с конкретных весов и профиля запросов. Исходные требования self-hosted LLM включают длину входа и ответа, конкурентность, рантайм и схему offload. Расчёт по размеру весов и формуле KV-кэша даёт стартовую конфигурацию, а прогретый тест показывает реальное потребление памяти и точку, где CPU перестаёт помогать.
Сколько RAM нужно квантованной модели?
RAM должна вмещать веса, KV-кэш, рабочие буферы рантайма и запас ОС. Основой служит размер файлов квантованной модели, а число параметров даёт лишь предварительную оценку. Итог подтверждают прогретым запуском: измеряют peak RSS/PSS и memory.current без swap при целевом контексте, конкурентности и максимальной длине ответа.
Зафиксировать модель, контекст и конкурентность
Название семейства вроде Qwen или Llama особо ничего не даёт. Вместо этого записывают репозиторий и revision, хеш файлов, формат, квантование, версию рантайма, chat template и tokenizer. Для MoE-модели отдельно указывают общее и активное число параметров.
Профиль трафика содержит p50, p95 и максимум входных токенов, длину ответа, число активных клиентов и настройки batch. Интерактивный режим оценивают по TTFT и межтокенной задержке. Для пакетного режима важны общий throughput и время выполнения задания.
Так требования self-hosted LLM привязывают ресурсы к конкретному сценарию. Один пользователь с контекстом 4K расходует KV-кэш и CPU не так, как восемь одновременных последовательностей по 4K. При неизвестном профиле лучше проверить несколько заданных сценариев, а не усреднять их в искусственный запрос.
Считать память модели по реальному файлу и схеме загрузки
Первую оценку даёт сумма shard-файлов, тогда как формула через число параметров и bits-per-weight не учитывает таблицы квантования, метаданные, embeddings и тензоры с другой точностью. Расчёт RAM для LLM начинается с размера конкретного артефакта.
Размер файлов нельзя просто складывать с RSS, потому что при mmap одни и те же резидентные страницы видны и в page cache, и в отображении процесса. PSS распределяет общие страницы между процессами. Без mmap рантайм может создать отдельную копию весов. При GPU-offload часть тензоров и буферов переносится в VRAM. Точное распределение видно в логе backend.
Базовый замер охватывает загрузку, прогрев и пик целевого запроса. RSS/PSS дополняют memory.current, swap, page faults и строки лога о CPU-, GPU-, KV- и compute-буферах, чтобы увидеть размещение весов и возможные копии.
Оценить рост памяти от контекста и конкурентности
Базовую оценку для полного KV-кэша можно записать так:
KV_bytes = 2 × L × H_kv × D_head × B_cache × T × S
Множитель 2 учитывает key и value, а остальные переменные задают слои, KV-головы, размер головы, байты на элемент, токены и последовательности. В GQA или MQA используют уменьшенное число KV-голов, причём точность кэша может отличаться от квантования весов.
Формула предполагает полный отдельный контекст для каждой последовательности, хотя paged cache, prefix sharing, sliding-window attention, гибридные слои и статическое резервирование меняют аллокацию. Метрика занятого KV-пула показывает расхождение с расчётом. Сколько vCPU нужно языковой модели, становится понятно после расчёта памяти и объёма prompt processing.
Когда дополнительные vCPU перестают ускорять инференс?
Дополнительные vCPU перестают помогать, когда генерация упирается в пропускную способность памяти или потоки пересекают NUMA-узлы. Prompt processing и generation насыщаются при разном числе ядер. Поэтому прогон по числу потоков делают для обеих фаз: нагрузку на каждом шаге держат одинаковой, а affinity, SMT и версию рантайма записывают.
Добавить рантайм, ОС и безопасный запас
Бюджет RAM складывается из резидентных весов, KV-пула, compute-буферов, служебной памяти рантайма и резерва ОС. Batch и длинный промпт поднимают пик за счёт временных тензоров. Память квантованной модели измеряют после прогрева: часть страниц выделяется только при первом обращении, и до этого RSS занижен.
Единого процента запаса для всех систем нет, тут нижнюю границу задаёт разница между steady-state и повторяемым peak RSS. Rolling update может временно держать две версии модели и увеличивать page cache, поэтому такой сценарий учитывают в запасе либо выносят в отдельное окно.
Контрольный прогон выполняют без swap, который скрывает дефицит RAM ценой задержки. Конфигурация подходит, если memory.current остаётся ниже лимита на величину заявленного запаса, разница memory.events до и после не содержит max/oom, PSI memory не растёт, а p95 и p99 выполняют SLO.
Развести parallel prompt processing и memory-bound generation
Обработка промпта вычисляет много токенов параллельно и обычно лучше загружает ядра CPU. Авторегрессионная генерация добавляет по токену на последовательность, а при небольшом batch её предел часто задаёт чтение весов из памяти. При росте конкурентности вычислительные блоки загружаются иначе, поэтому результат одного клиента не переносится на серверный режим.
В llama.cpp параметр -t задаёт потоки генерации, а -tb, доступный в llama-server и llama-cli, отдельно управляет потоками batch и обработки промпта. В llama-bench такого разделения нет, поэтому фазы разносят по отдельным прогонам. Для обработки промпта сохраняют tokens/s и время выполнения, клиент отдельно измеряет TTFT. Для генерации нужны tokens/s, p95 межтокенной задержки и суммарный throughput. Загрузка CPU лишь дополняет эти метрики, поскольку высокий процент не доказывает полезную работу.
Во время CPU-серии сайзинг KV-кэша остаётся неизменным. Отчёт содержит хеш модели, квантование, длины контекста и ответа, конкурентность, версию рантайма, RSS/peak memory, параметры кэша, топологию vCPU и результаты обеих фаз, чтобы изменившийся batch не выдавали за прирост от ядер.
Найти полезный предел физических ядер
Команда lscpu -e=CPU,CORE,SOCKET,NODE,ONLINE показывает топологию гостя, которая не обязательно совпадает с физическим размещением на хосте. Также записывают %steal, CPU model, политику частоты, affinity и сведения о pinning, если они доступны.
В ходе thread sweep число доступных ядер увеличивают ступенями. На каждой ступени берут ту же сборку и повторяют тесты prompt processing и generation, а порядок прогонов между ступенями чередуют. Для llama.cpp подойдёт цикл с
llama-bench: : > thread-sweep.jsonl for t in 1 2 4 8; do llama-bench -m model.gguf -p 512 -n 128 \ -t "$t" -r 5 -o jsonl \ >> thread-sweep.jsonl done
Набор ступеней подбирают под конкретную VM и не выходят за пределы cpuset. Результаты каждого шага включают медиану и разброс tokens/s. Серверные метрики добавляют показатель CPU time, а отдельный клиентский прогон даёт TTFT и p95/p99 задержки. Полезный предел достигнут, когда прирост от новых vCPU не превышает разброса между повторами или когда SLO ухудшается из-за синхронизации потоков и роста steal.
Как учесть длину контекста и конкурентность?
Для каждого режима зафиксируйте p95 входных и выходных токенов, число активных последовательностей и batch.
Подставьте эти значения в формулу KV-кэша с параметрами архитектуры и точностью кэша.
Сверьте оценку с логом рантайма, peak RSS и задержками прогретого прогона без swap.
Так планирование мощности локальной LLM даёт проверяемую оценку для каждого режима. Изменение квантования, контекста, batch или конкурентности требует нового прогона, так как точка насыщения может сдвинуться даже на той же VM.
Проверить bandwidth и размещение большой модели
Большая CPU-модель постоянно обращается к памяти, поэтому число vCPU оценивают вместе с топологией. numactl --hardware показывает NUMA-узлы гостя, а вывод numastat -p PID отражает распределение страниц процесса. Реальное размещение VM подтверждается только на стороне хоста или по данным платформы.
Сравнение требует одинаковой affinity и политики памяти. Процесс сначала удерживают в одном узле, затем проверяют межузловое размещение. При падении tokens/s или росте p99 проверяют обращения к памяти чужого узла и memory bandwidth, а вычислительные потоки могут быть ни при чём.
Отдельный тест памяти даёт фактическую пропускную способность, которую сопоставляют со скоростью generation. На VPS аппаратные счётчики могут быть недоступны, а соседние VM меняют доступный ресурс. Для каждой серии сохраняют время, %steal и результаты повторов.
Собрать проверяемые размеры для трёх режимов
Итоговая таблица не содержит универсальных чисел, здесь в каждой строке приведены результаты отдельного профиля запросов.
Режим |
Профиль |
Бюджет RAM |
Критерий CPU |
Один пользователь |
Одна последовательность, p95 input/output |
Peak RSS, один KV-контекст, headroom |
TTFT и generation до плато |
Интерактивный |
Рабочая конкурентность и очередь |
Занятый KV-пул, peak RSS, запас SLO |
p95/p99 latency и общий throughput |
Batch |
Зафиксированные batch и длины |
Peak RSS пакета и резерв обновления |
Throughput и время задания |
При одном пользователе важны TTFT и скорость генерации. Профиль нескольких интерактивных клиентов включает p95/p99, очередь и занятый KV-пул. В batch-режиме допустима большая задержка задания, если общий throughput растёт в пределах бюджета RAM.
Больше RAM нужно, если повторяемый пик с запасом приближается к лимиту или появляются reclaim и swap. Новые vCPU имеет смысл добавлять, пока кривая нужной фазы растёт. Если увеличение threads уже не помогает, следующий тест проводят с более быстрым CPU или памятью, другим NUMA-размещением, GPU-offload либо иной политикой batch и KV-cache.
К результату прикладывают версии, входные данные, таблицу и сырой JSONL. Новый профиль нужен при смене модели, рантайма или конкуренции. По такому профилю ресурсы пересчитывают до покупки VM, а после развёртывания подтверждают тем же сценарием.
Итоговый сайзинг RAM и vCPU для локальной языковой модели опирается на измеренное потребление плюс заложенный запас. Размер файла и формула KV-кэша дают исходную оценку, а peak RSS/PSS, memory.current и thread sweep уточняют её на конкретной VM. По ядрам граница своя: после насыщения целевой фазы новые vCPU скорость уже не поднимают. Если SLO не выполняется после плато CPU, причину ищут в памяти, NUMA, GPU или рантайме.