MAATRIX / Блог / Ollama медленно генерирует токены: причины и решение

Ollama медленно генерирует токены: причины и решение

Ollama медленно генерирует токены: причины и решение

MAATRIX

Модель выдаёт по слову в секунду, курсор ползёт, а htop показывает загрузку процессора в 30% — знакомая картина. Ollama почти никогда не «тормозит сама по себе»: за медленной генерацией стоит одна из четырёх конкретных причин, и каждая диагностируется за пару минут. Разберём, как найти свою по цифрам, а не по ощущениям, и что реально даёт прирост, а что плацебо.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Сначала цифры: `--verbose` и eval rate

Пока нет числа токенов в секунду, обсуждать нечего. Ollama показывает его сам — запустите модель с флагом и задайте вопрос:

ollama run llama3.1:8b --verbose

После ответа вы увидите блок статистики:

total duration:       1m12.445s
load duration:        8.214s
prompt eval count:    1462 token(s)
prompt eval duration: 31.208s
prompt eval rate:     46.85 tokens/s
eval count:           412 token(s)
eval duration:        33.021s
eval rate:            12.48 tokens/s

Читать это надо так:

  • load duration — загрузка весов с диска. Заметной должна быть только в первом запросе; если она в каждом — модель выгружается между ними, это шестая секция.
  • prompt eval rate — обработка вашего промпта. Считается пакетно, упирается в вычисления: на CPU это 30–60 tok/s, на GPU — тысячи.
  • eval rate — собственно генерация, строго по одному токену. Именно эта цифра и есть «скорость Ollama».

Тормозят они по разным причинам: задержка в prompt eval duration лечится кэшем префикса и коротким системным промптом, а низкий eval rate — это память, и ядра тут не помогут. Для мониторинга те же поля отдаёт API: в ответе /api/generate есть eval_count и eval_duration в наносекундах.

Запишите полученное число. Норма для вашего железа считается в следующей секции: попали в неё — ускорять надо железо, вдвое ниже — ищите потерю в разделах 3–6.

Скорость упирается не в ядра, а в пропускную способность памяти

Главное про инференс: чтобы выдать один токен, движок обязан прочитать все веса модели. Модель 8B в Q4_K_M — это 4,9 ГБ через шину памяти на каждый токен. Отсюда потолок:

eval rate ≈ пропускная способность памяти ÷ размер весов × 0,6–0,75

Коэффициент — КПД реальной системы против паспорта. Подставляем:

ПамятьПиковая полоса8B Q4_K_M (4,9 ГБ)70B Q4_K_M (40 ГБ)
DDR4-3200, 2 канала51 ГБ/с6–9 tok/s0,7–1 tok/s
EPYC, 8 каналов DDR4-3200205 ГБ/с25–33 tok/s3–4 tok/s
RTX 4090, GDDR6X1008 ГБ/с110–140 tok/sне помещается
A100 80 ГБ, HBM2e2039 ГБ/с180–250 tok/s25–35 tok/s

Два следствия. Первое: переход с 8 vCPU на 16 почти ничего не даёт, если каналы памяти те же — полоса не выросла, потолок остался (разбор в статье Ollama не использует все ядра CPU). Второе: разрыв между CPU и GPU — не «в два раза», а в 15–20 раз, и он структурный.

Свою полосу измерьте так: sysbench memory --memory-block-size=1M --memory-total-size=32G --memory-oper=read --threads=8 run. На здоровом двухканальном DDR4 будет 30 000–45 000 MiB/sec. Сильно меньше — вы на переподписанном узле и делите шину с соседями. Полосу памяти нельзя «настроить», её можно только купить другую.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Ollama

Модель не поместилась: частичная выгрузка и своп

Самая частая причина «было 120 tok/s, стало 9» — модель перестала помещаться целиком, и часть слоёв уехала на CPU. Проверяется командой ollama ps:

NAME           ID              SIZE      PROCESSOR          UNTIL
llama3.1:8b    46e0c10c039e    11 GB     38%/62% CPU/GPU    4 minutes from now

Строка 38%/62% CPU/GPU — это диагноз. Скорость связки определяет медленная половина: даже 20% слоёв на процессоре срезают генерацию со 130 до 20–25 tok/s. Промежуточного не дано: либо модель целиком в VRAM, либо это CPU с надбавкой.

Решение планировщика видно в журнале — sudo journalctl -u ollama -n 200 --no-pager | grep -i offload:

level=INFO source=server.go:168 msg=offload library=cuda layers.requested=-1
  layers.model=33 layers.offload=21 memory.available="[11.6 GiB]"
  memory.required.full="15.1 GiB" memory.required.partial="11.2 GiB"

Нужно 15,1 ГиБ, свободно 11,6 — в видеопамять уехал 21 слой из 33. Дальше арифметика: уменьшить memory.required.full (квантование, контекст, KV-кэш) либо увеличить memory.available. Проверьте заодно nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv: часто VRAM занял посторонний процесс.

На CPU-сервере аналог этой беды — своп: когда памяти не хватает, ядро возит страницы на диск, и генерация падает до долей токена в секунду. Смотрите vmstat 1 во время запроса:

 r  b   swpd   free   buff  cache   si   so    bi    bo  us sy id wa st
 2  1 3145728  98432  1204 512300 21504 18432 42112 19200 18  9  6 67  0

Ненулевые si/so и wa под 67% — модель живёт в свопе. Настройками не лечится: либо модель меньше, либо больше RAM. «Добавить своп побольше» не работает: NVMe медленнее DDR4 примерно в сто раз. Если памяти не хватает совсем, Ollama скажет прямым текстом: Error: model requires more system memory (9.3 GiB) than is available (5.1 GiB). Сколько её нужно на какую модель — таблица в материале Сколько RAM нужно для Ollama.

Квантование: сколько стоит каждый лишний бит

Раз скорость обратно пропорциональна размеру весов, квантование — самый дешёвый рычаг. Проверьте, что скачано:

ollama show llama3.1:8b
  Model
    architecture        llama
    parameters          8.0B
    context length      131072
    quantization        Q4_K_M

Тег без суффикса даёт Q4_K_M — разумный дефолт. Но люди регулярно тянут :8b-instruct-q8_0 «чтобы получше» и получают вдвое меньшую скорость. Замеры на одном сервере с 8 vCPU и DDR4-3200:

КвантованиеРазмер файлаeval rateКачество
q8_08,5 ГБ3,5 tok/sэталон
q6_K6,6 ГБ4,6 tok/sотличий почти нет
q5_K_M5,7 ГБ5,4 tok/sотличий почти нет
q4_K_M4,9 ГБ6,8 tok/sрабочий баланс
q3_K_M4,0 ГБ8,1 tok/sзаметно деградирует

Честный вывод: выше Q6 платить за качество бессмысленно, ниже Q4_K_M — опасно. Первым при агрессивном сжатии ломается код, следом — работа с числами и длинными инструкциями. Не влезает в Q4_K_M — берите модель поменьше в Q4, а не ту же в Q3: 8B в Q4_K_M осмысленнее, чем 14B в Q3_K_S. Переключение — ollama pull llama3.1:8b-instruct-q4_K_M и ollama rm для старого тега.

Контекст и KV-кэш — тихий пожиратель скорости

Второй по частоте случай «сам себе навредил»: человек ставит num_ctx в 32768 на всякий случай и удивляется, куда делась скорость. Дело в KV-кэше — он занимает память дополнительно к весам и растёт линейно по длине контекста.

Для Llama 3.1 8B (32 слоя, 8 KV-голов, размерность головы 128, кэш в fp16) на один токен уходит 32 × 8 × 128 × 2 × 2 = 131 072 байта, ровно 128 КиБ. Дальше просто:

КонтекстKV-кэш fp16KV-кэш q8_0
4 0960,5 ГиБ0,25 ГиБ
8 1921 ГиБ0,5 ГиБ
32 7684 ГиБ2 ГиБ
131 07216 ГиБ8 ГиБ

Те самые 4 лишних гигабайта на карте с 12 ГБ VRAM и выталкивают треть слоёв на процессор. По умолчанию Ollama берёт 4096 токенов; если надо больше — поднимайте ровно до нужного, а не до максимума модели:

OLLAMA_CONTEXT_LENGTH=8192

В интерактивной сессии — /set parameter num_ctx 8192, через API — "options": {"num_ctx": 8192}. Переменная OLLAMA_CONTEXT_LENGTH доступна начиная с 0.6, проверьте свою через ollama -v.

Второй рычаг — квантование самого кэша: требует включённого flash attention и режет память вдвое (q8_0) или вчетверо (q4_0):

OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0

q8_0 на практике неотличим от fp16 — берите смело. q4_0 экономит больше, но на длинных диалогах модель теряет детали из начала контекста: для чата терпимо, для работы с документами — нет.

Отдельно про prompt eval count: если в продолжении диалога оно равно всей истории, а не десятку новых токенов, кэш префикса не сработал и переписка пересчитывается заново каждый ход. Причины обычно две — меняющийся системный промпт (подставляемая дата, случайный идентификатор) или параллельные слоты, вытесняющие кэш друг друга.

Перезагрузка модели, keep_alive и лишний параллелизм

Вернитесь к load duration из первой секции: если она 8 секунд и появляется в каждом запросе, вы платите их постоянно. По умолчанию Ollama выгружает модель через 5 минут простоя — для сервера, который отвечает раз в десять минут, это худший режим.

Настройки задаются переменными окружения службы. Правильное место — drop-in systemd:

sudo systemctl edit ollama
[Service]
Environment="OLLAMA_KEEP_ALIVE=24h"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_CONTEXT_LENGTH=8192"
Environment="OLLAMA_NUM_PARALLEL=1"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
sudo systemctl daemon-reload && sudo systemctl restart ollama
systemctl show ollama --property=Environment

Последняя команда обязательна — она показывает, что переменные доехали до процесса. Половина случаев «не помогло» это экспорт в ~/.bashrc: служба стартует не из вашего шелла.

Что делает каждая строка:

  • OLLAMA_KEEP_ALIVE=24h — модель остаётся в памяти (-1 — бесконечно). Обратная сторона: занятая VRAM никому больше не достанется.
  • OLLAMA_NUM_PARALLEL=1 — Ollama готовит несколько слотов параллельных запросов, и объём KV-кэша умножается на их число. Для одного клиента это потеря памяти и повод выгрузить слои на CPU.
  • OLLAMA_MAX_LOADED_MODELS=1 — две модели в VRAM вытесняют друг друга, и load duration возвращается при каждом переключении.

Веса лежат в /usr/share/ollama/.ollama/models (при ручном запуске — в ~/.ollama/models). NVMe читает их на 1,5–3 ГБ/с и поднимает модель 5 ГБ за 2–4 секунды, сетевое хранилище на 150 МБ/с — за полминуты.

Какой сервер брать под Ollama в MAATRIX

Честный минимум для CPU-инференса: 8 vCPU, 16 ГБ RAM, NVMe. Модель 8B в Q4_K_M с контекстом 8k выдаёт на нём 6–9 tok/s — хватает для фоновых задач: суммаризация, классификация писем, разбор документов по расписанию, локальный RAG. Для живого чата это медленно, и притворяться иначе смысла нет.

Память считайте по формуле вес модели + KV-кэш + 2 ГБ на систему плюс запас на страничный кэш. Для 8B с контекстом 8k выходит 4,9 + 1 + 2 ≈ 8 ГБ, но брать надо 16 ГБ, иначе веса вымываются из кэша. Для 14B комфортно 32 ГБ, для 32B — 64 ГБ, но там скорость на CPU падает до 2–3 tok/s, и это уже разговор про GPU.

Комфортный вариант — сервер с видеокартой, где VRAM больше суммы «веса + KV-кэш». 24 ГБ закрывают 8B–14B на полной скорости и 32B в Q4 впритык. Разница не косметическая: 8–9 tok/s против 110–140. Для интерактивного интерфейса и нескольких пользователей альтернатив, по сути, нет, а на десятках параллельных запросов смотреть надо уже не на Ollama — сравнение в материале Ollama против vLLM.

Локация под эту задачу — UK, Лондон, по двум прикладным причинам. Первая: веса с реестра качаются на полной скорости канала, модель 20 ГБ приезжает за минуты, а не за вечер. Вторая: пинг из Москвы до Лондона 45–60 мс — для потоковой отдачи токенов незаметно, зато клиентам из ЕС минимальная задержка и GDPR-контур. Франция закрывает тот же сценарий, российская площадка нужна при требованиях 152-ФЗ, американская — если рядом живёт ещё и прокси к внешним ИИ-API.

До заказа снимите eval rate с --verbose и посмотрите ollama ps: упор в настройки — половина статьи решает вопрос бесплатно, упор в железо — конфигурацию соберём под конкретную модель, а не «на глаз». Оплата картой российского банка, по СБП, криптой или токеном MAAT; иностранная карта для зарубежной площадки не нужна. Базовая установка разобрана в статье Как поднять локальный запуск LLM через Ollama.

Оговорка напоследок: на общем VPS с переподпиской eval rate плавает на ±30% в зависимости от соседей по узлу. Нужна предсказуемая скорость — берите тариф с выделенными ядрами или отдельный сервер.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Ollama

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Ollama медленно работает — поможет ли добавить vCPU?

Почти нет. Генерация упирается в пропускную способность памяти, а не в вычисления: переход с 8 ядер на 16 при тех же каналах памяти даёт единицы процентов. Помогает больше каналов памяти или GPU.

Почему первый ответ идёт 10 секунд, а следующие быстро?

Это load duration — загрузка весов с диска. По умолчанию модель выгружается через 5 минут простоя; поставьте OLLAMA_KEEP_ALIVE=24h через systemctl edit ollama, и задержка исчезнет.

Я поднял контекст до 32k, и скорость упала втрое. Это баг?

Нет. KV-кэш вырос на несколько гигабайт, модель перестала помещаться в VRAM, и часть слоёв уехала на CPU — это видно в ollama ps как 38%/62% CPU/GPU. Верните контекст к реально нужному и включите OLLAMA_KV_CACHE_TYPE=q8_0.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.