Перейти к основному содержимому

Оптимизация локального инференса LLM

Продвинутый

Локальная модель уже отвечает, но текст появляется медленно, сессия падает с нехваткой памяти или MoE-модель то летит, то ползёт. Эта статья про то, что именно крутить после первого запуска через Работу с ИИ-моделями.

Практические замеры и флаги llama.cpp собраны по мегагайду Kartikey Chauhan (RTX 4070 12 GB, i5-12600K, 32 GB DDR5). Цифры в таблицах — ориентир; на вашем ПК будут свои.

С чего начать, если вы ещё не ставили модель


Словарь перед настройкой

ТерминПростыми словами
ИнференсРабота уже обученной модели на вашем запросе (теория)
VRAMВидеопамять GPU. Сюда же попадает KV-кэш
GGUFФайл весов для llama.cpp / Ollama (форматы в 113)
КвантизацияСжатие весов (Q4, Q5, Q8). Меньше гигабайт — чуть ниже качество
КонтекстСколько токенов модель держит в "памяти" за один диалог (статья про контекст)
KV-кэшБуфер промежуточных данных внимания; растёт с длиной контекста
MoEMixture of Experts — большая модель, но на каждый токен считается только часть "экспертов" (Mixture of Experts)
OffloadЧасть слоёв на GPU, часть в оперативной памяти
TGToken generation — скорость выдачи ответа (токенов в секунду). Именно её чувствует пользователь
TTFTTime to first token — пауза до первого слова ответа
PPPrompt processing — как быстро модель "прочитала" ваш промпт
OOMOut of memory — закончилась VRAM или RAM
XMP / EXPOПрофиль в BIOS, чтобы оперативная память работала на заявленной частоте (DDR5-6000 и т.д.)
MTPMulti-Token Prediction — ускорение за счёт черновика нескольких токенов сразу

Что измерять

Сначала поймите, какая фаза тормозит. Иначе можно неделю крутить batch, а узкое место — частота RAM.

МетрикаЧто показываетГде обычно упирается
TTFTЗадержка до первого токенаЗагрузка файла модели, длинный промпт
PPСкорость обработки входаРазмер batch, GPU
TGСкорость потока ответаVRAM, RAM, размещение слоёв
VRAM при стартеВлезли ли веса и KV-кэшДлина контекста, --parallel, квант
VRAM через час работыРост до OOMМаленький запас --fit-target, CUDA graphs
Acceptance rate (при MTP)Сколько черновиков приняла основная модельТочность KV у draft и target

Тестируйте на той же длине контекста, с которой работаете. Короткий промпт из 50 токенов не покажет, как ведёт себя RAG на 64k.

В сборке llama.cpp есть llama-bench — синтетические замеры PP и TG.

Скорость ответа глазами пользователя

TG, токен/сКак ощущается
меньше 5Тяжело читать в реальном времени
5–10Похоже на скорость чтения
10–20Нормальный чат
20–40Быстро, удобно для кода и агентов
40 и вышеОтвет "льётся" почти сразу

Какой движок выбрать

ИнструментПодходит для
OllamaБыстрый старт, Docker, мало настроек
LM StudioОкно с чатом и локальный OpenAI API
llama.cppВсе флаги из этой статьи, максимум контроля
vLLMМного одновременных пользователей на сервере
mlxMac на Apple Silicon

Ollama внутри использует llama.cpp, но не открывает ряд важных параметров (--fit, квантизацию KV, batch, переменные CUDA). Когда Ollama/LM Studio упёрлись в потолок — поднимают llama-server напрямую.

Дальше в тексте — llama.cpp на NVIDIA (CUDA). Идеи про KV-кэш и квант переносятся на другие рантаймы; имена флагов — только у llama.cpp.

Бэкенды llama.cpp при сборке

  • CUDA (-DGGML_CUDA=ON) — видеокарты NVIDIA
  • Vulkan (-DGGML_VULKAN=ON) — AMD, Intel, кроссплатформа
  • Metal — macOS, Apple Silicon
  • CPU — без GPU-флага; отладка и очень маленькие модели

Чеклист по убыванию эффекта

Типичный порядок на домашнем ПК (сверху — чаще всего даёт заметный прирост):

#ДействиеЗачем
1Включить XMP/EXPO в BIOSMoE читает экспертов из RAM; медленная RAM режет TG в разы
2MTP (если модель поддерживает)Несколько токенов за один проход GPU
3Сборка QAT (например Gemma 4 Q4 QAT)Меньше VRAM при качестве ближе к Q8
4Linux или план "Высокая производительность" в WindowsМеньше троттлинга CPU и CUDA
5Сборка llama.cpp из исходниковСвежие ядра для MoE
6--fit onАвтоматически кладёт слои на GPU под ваш VRAM
7-ctk q8_0 -ctv q8_0KV-кэш в два раза меньше — место под слои
8--parallel 1Один слот KV вместо нескольких
9taskset на P-ядра IntelE-ядра тормозят MoE
10--flash-attn onМеньше памяти на длинном контексте
11--no-mmap и --mlockРовный TG без page fault и swap

Железо и иерархия памяти

Модель на каждом шаге перечитывает веса из памяти. Чем быстрее шина — тем выше TG.

VRAM (видеопамять) ~600–1000 ГБ/с
Unified (Apple Silicon) ~200–400 ГБ/с
ОЗУ DDR4/DDR5 ~50–200 ГБ/с
NVMe SSD ~5–7 ГБ/с

Плотная модель (Llama, Mistral без MoE)

  • все веса участвуют в каждом токене;
  • для полной скорости их держат в VRAM;
  • spill в RAM сильно бьёт по TG.

MoE (Mixtral, Qwen3-MoE, DeepSeek)

  • общий размер может быть 70B–120B, но активных параметров на токен — единицы миллиардов;
  • эксперты часто лежат в оперативной памяти;
  • без XMP/EXPO RAM может работать на 4800 МГц вместо 6000 — и TG падает кратно.

Проверка частоты RAM в Linux:

sudo dmidecode -t memory | grep -E "Speed|Configured"

Строка Configured Memory Speed должна совпадать с наклейкой на планках (профиль XMP/EXPO в BIOS).

Сколько VRAM на какую модель

VRAMПлотные модели (ориентир)MoE с offload в RAM
8 GB7B–13B в Q430B–70B, большая часть на CPU
12 GB13B–20B Q4; 7B Q870B–120B+
24 GB34B Q4; 13B Q8120B+ с умеренным offload
48 GB+70B Q8Большая доля слоёв на GPU

Подробнее про выбор размера — Как выбрать модель.

Освободить VRAM под модель

  • подключить монитор к встроенной графике материнской платы;
  • дискретная GPU остаётся только для инференса;
  • обычно +500–1000 MB VRAM.

CPU на гибридных Intel (12-го поколения и новее)

У таких процессоров есть P-ядра (быстрые) и E-ядра (экономичные). MoE на каждом токене гоняет экспертов через CPU — если процесс попал на E-ядра, TG падает на 20–30%.

Привязка к P-ядрам (пример i5-12600K, ядра 0–11):

taskset -c 0-11 llama-server ...
  • --threads — примерно число P-ядер минус 1–2 под ОС;
  • потоков больше, чем P-ядер, — обычно хуже, а не лучше.

ОС и питание

Linux

На том же железе часто даёт на 15–20% больше токенов в секунду, чем Windows: проще выставить режим CPU и убрать лишнее с GPU.

Минимальная проверка:

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference

Оба значения — performance.

В KDE/GNOME часто стоит power-profiles-daemon. Симптом: в sysfs всё performance, а TG то 20 tok/s, то 14 после перезагрузки. Помогает tuned с профилем throughput-performance (пакет tuned-ppd в Arch/CachyOS).

Режим без графического рабочего стола (headless) освобождает 200–400 MB RAM и VRAM композитора:

sudo systemctl isolate multi-user.target
sudo systemctl isolate graphical.target # вернуть GUI

Windows

  • Параметры питания → Высокая производительность или Максимальная производительность
  • NVIDIA Control Panel → Power management mode → Prefer maximum performance
  • WSL2 с CUDA близок к нативному Linux; небольшой overhead по VRAM

macOS и Apple Silicon

Советы про CUDA сюда не переносятся. Сравните llama.cpp с бэкендом Metal и фреймворк mlx. Unified memory — CPU и GPU делят один пул RAM (выбор железа).


Сборка llama.cpp

Пакет из репозитория дистрибутива часто отстаёт на месяцы. MoE заметно ускоряли от релиза к релизу — собирайте с GitHub:

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp && mkdir build && cd build

cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_CUDA=ON \
-DLLAMA_CURL=ON \
-DGGML_NATIVE=ON \
-DGGML_LTO=ON \
-DGGML_CUDA_GRAPHS=ON \
-DGGML_CUDA_FA=ON \
-DGGML_CUDA_FA_ALL_QUANTS=ON \
-DCMAKE_CUDA_ARCHITECTURES=89
# 89 — RTX 40xx; 86 — 30xx; 75 — 20xx

cmake --build . --config Release \
--target llama-server llama-bench llama-fit-params llama-cli --parallel
БинарникДля чего
llama-serverHTTP-сервер, OpenAI-совместимый API
llama-benchЗамеры PP и TG
llama-fit-paramsПодбор -ngl и -ot без запуска сервера
llama-cliБыстрый тест в терминале

Документация по флагам сервера — README llama.cpp.


Модель и квантизация

Плотные модели и MoE

Плотная (dense)MoE
Параметров на один токенВсеМалая доля (например 3B из 80B)
Нужна ли вся модель в VRAMЖелательно для max TGНет, эксперты в RAM
Главные рычагиКвант, длина контекста--fit, скорость RAM
ПримерыLlama 3, Mistral, GemmaQwen3, DeepSeek, Mixtral

На маркетинге "120B" для MoE смотрите на активные параметры — от них зависит TG.

Уровни GGUF

КвантРазмер относительно FP16Качество
Q2_K~25%Сильная потеря, только если иначе не влезает
Q4_K_M~35%Рабочий баланс
Q4_K_XL, UD-Q4~35%Часто лучше Q4_K_M при том же размере
Q5_K_M, Q5_K_XL~40%Близко к оригиналу
Q8_0~65%Практически без потерь

PTQ — квантизация уже обученных весов. QAT (quantization-aware training) — модель учили с учётом округления; Q4 QAT у Gemma часто по смыслу ближе к Q8 PTQ и занимает меньше VRAM. На 12 GB это разница между "всё в RAM" и "большинство слоёв на GPU".

Правило: берите самый тяжёлый квант, который влезает в VRAM + RAM вместе с выбранным контекстом.

Подробнее про Q4/Q5 в 113.


Размещение слоёв на GPU и CPU

Для плотной модели, которая целиком в VRAM, достаточно -ngl 99 (все слои на GPU) и блока про KV-кэш.

Для MoE главный выигрыш — сколько блоков transformer остаётся на GPU, а какие тензоры экспертов уходят в RAM.

Авторазмещение (--fit on)

Сервер при старте смотрит свободный VRAM и подбирает -ngl и -ot:

llama-server \
-m model.gguf \
--fit on \
--fit-ctx 65536 \
--fit-target 512 \
...
  • --fit-ctx — минимальный контекст, под который резервируют KV-кэш
  • --fit-target — запас VRAM в мегабайтах; для постоянного сервера ставьте 512 и выше; для картинок (mmproj) на 12 GB стабильнее 2048

Без запуска сервера:

llama-fit-params -m model.gguf -fitt 512 -fitc 65536

Вывод можно вписать в systemd-скрипт. --fit on оставьте для экспериментов с новой моделью.

Ручные флаги

  • -ngl / --n-gpu-layers — число блоков на GPU
  • -ot / --override-tensor — regex, какие тензоры на CPU:
--override-tensor ".ffn_(up|down|gate)_(ch|)exps=CPU"

У части моделей есть shared expert (_shexp) — он активен всегда. Паттерн только для _exps оставляет shared на GPU, съедает VRAM и даёт OOM. Фрагмент (ch|)exps покрывает оба варианта.

--n-cpu-moe N — грубо "сколько MoE-слоёв оставить на CPU". На машине с 64 GB RAM и моделью ~60 GB можно уйти в swap и подвесить систему — сначала llama-fit-params.


Контекст и KV-кэш

При авторегрессии модель на каждом шаге смотрит на все предыдущие токены. Чтобы не пересчитывать внимание заново, KV-кэш хранит уже посчитанные Key и Value. Он живёт в VRAM и растёт вместе с длиной диалога (промпт + ответ + RAG-чанки).

Ориентир по VRAM только на KV (зависит от модели):

КонтекстKV в f16KV в q8_0
8k~0.5 GB~0.25 GB
32k~2 GB~1 GB
64k~4 GB~2 GB
128k~8 GB~4 GB

На карте 12 GB и контексте 128k с f16 до 8 GB уходит только на кэш. Для кода часто хватает 64k; для длинного RAG — 128k, но осознанно (GraphRAG тоже ест контекст).

Квантизация KV (-ctk, -ctv)

-ctk q8_0 -ctv q8_0

KV занимает примерно вдвое меньше, чем в f16. Освободившиеся 1–2 GB часто позволяют держать ещё один слой на GPU — прямой плюс к TG.

--parallel N — число параллельных диалогов на одном сервере. У каждого свой KV-кэш. Один пользователь в homelab → --parallel 1.

Flash Attention (--flash-attn on)

Механизм внимания без полной матрицы N×N в памяти. На длинном контексте и маленьком VRAM включают почти всегда. Подробнее — трансформеры и оптимизации.

Стартовые профили (один пользователь)

Сценарийfit-targetКонтекстKVparallelbatch
Текст, 12 GB512 MiB64kq8_0 / q8_011024
Текст, 24 GB512–768 MiB128kq8_0 / q8_01–21024
Картинки, 12 GB512+ MiB64kq8_0 / q8_01256
MTP512+ MiB64kсм. раздел MTP11024

Память процесса

--no-mmap # загрузить веса в RAM целиком до старта генерации
--mlock # запретить ОС сбрасывать страницы в swap
  • mmap — файл модели подключается к памяти "лениво"; при random access к экспертам MoE возможны микропаузы (page fault)
  • mlock — важен при высоком vm.swappiness и модели впритык к объёму RAM

Старт дольше, зато TG ровнее на длинных сессиях.


MTP и speculative decoding

Обычная генерация — один токен за один полный проход всех весов. Speculative decoding угадывает несколько следующих токенов лёгкой моделью; основная проверяет их за один шаг.

MTP (Multi-Token Prediction) — вариант для моделей, у которых есть официальный draft (Gemma 4, часть Qwen). Пример замеров Gemma 4 26B на RTX 4070 12 GB (пост автора):

КонфигурацияTG
Без MTP~38.5 tok/s
QAT + MTP~100.6 tok/s
llama-server \
-m base-qat.gguf \
--spec-draft-model mtp-draft.gguf \
--spec-type draft-mtp \
--spec-draft-n-max 2 \
...
  • --spec-draft-n-max 2 — для тяжёлой MoE на 12 GB; на легче моделях пробуют 4–6
  • KV целевой модели — -ctk, -ctv; черновика-ctkd, -ctvd. На Gemma 4 q8_0 на target резал acceptance почти до нуля; f16 на обоих давал acceptance выше 70% и итоговый выигрыш. Для каждой модели — свой замер

Картинки и multimodal

Флаг --mmproj указывает на файл проектора — он переводит изображение в токены для LLM. Весит 1–3 GB VRAM.

На 12 GB рабочий набор:

  • --fit-target 2048
  • --batch-size 256
  • --ubatch-size 512 (одна картинка = сотни токенов; меньший ubatch → ошибка при загрузке)

Текст и vision с одной GPU удобнее развести по разным портам или разным инстансам llama-server. Мультимодальность в теории — трансформеры в разных модальностях.


Если TG всё ещё низкий

Проверьте по порядку:

  1. XMP/EXPO в BIOS
  2. CPU governor и EPP → performance
  3. Частота P-ядер под нагрузкой
  4. Свободный VRAM до старта (nvidia-smi)
  5. Swap (pswpin / pswpout в /proc/vmstat) в длинной сессии
  6. Температура CPU/GPU (троттлинг)
  7. Процесс не размазан по E-ядрам
СимптомЧастая причинаЧто попробовать
TG стабильно в 3 раза ниже ожидаемогоRAM не на номиналеXMP в BIOS
TG разный после каждой перезагрузкиpower-profiles-daemontuned, throughput-performance
Провалы через 20–40 минутswap--mlock, меньше модель
OOM в середине диалогамалый --fit-target512 MiB и больше
MTP не ускоряетнизкий acceptanceKV f16, меньше spec-draft-n-max

Безопасность локального сервера

llama-server, LM Studio и Ollama поднимают HTTP-сервис. Для доступа извне:

  • VPN (Tailscale, WireGuard);
  • reverse proxy с аутентификацией;
  • firewall и лимиты запросов.

Проброс порта 8080 на роутер без защиты — плохая идея.

Разбор рисков — Безопасность при работе с ИИ, OWASP LLM Top 10.


Куда читать дальше

Углубление по флагам и бенчмаркам — Local LLM Inference Optimization, серия Local Inference на том же сайте.