Локальная LLM отвечала минуту вместо секунды: где узкое место
Вчера модель отвечала за секунды, сегодня генерация одного ответа тянется минуту — а вы ничего не меняли. Первая мысль обычно «модель сломалась» или «сервер под нагрузкой», и обе версии почти всегда неверны. На практике за такой деградацией стоит одна из трёх-четырёх конкретных причин, и все они находятся за пять минут, если знать, куда смотреть.
Содержание
- Первая версия: "модель сломалась" — и почему это не так
- Диагностика по слоям: с чего начать
- Причина 1: контекст диалога вырос и съел KV-кеш
- Причина 2: фоновый процесс занял видеокарту
- Причина 3: обновился драйвер или библиотека — и ускорение отвалилось
- Как быстро локализовать причину без перебора
- Профилактика: мониторьте GPU и VRAM отдельно от общих метрик сервера
Первая версия: "модель сломалась" — и почему это не так
Когда ответ вместо секунды идёт минуту, первый порыв — перезапустить сервис, перекачать модель заново или откатиться на предыдущую версию. Это тратит время впустую в девяти случаях из десяти. LLM — это по сути один и тот же матричный расчёт на каждый токен генерации; если веса модели не повреждены (а битые веса дают мусорный текст или ошибку загрузки, а не медленную генерацию), сама модель ни в чём не виновата. "Сломалась" — это диагноз для процесса, который либо перестал получать GPU, либо стал упираться в CPU, либо конкурирует за ресурс с кем-то ещё. Разница между секундой и минутой на генерацию одного ответа — это разница на два порядка, а такую разницу даёт только смена вычислительного устройства или резкая нехватка памяти, а не "порча" модели как таковой.
Прежде чем трогать модель или конфиг, стоит зафиксировать факт: когда именно это началось (после перезагрузки сервера? после обновления пакетов? само по себе посреди дня?) и воспроизводится ли замедление на каждом запросе или только на длинных диалогах. Эти два ответа сразу отсекают половину версий.
Диагностика по слоям: с чего начать
Правило разбора любого перформанс-инцидента с ИИ-инфраструктурой — идти снизу вверх: сначала железо и драйвер, потом рантайм модели, потом уже приложение сверху. Три проверки, которые закрывают почти все случаи.
Проверка 1 — грузится ли вообще GPU в момент запроса. Откройте два терминала: в одном отправьте запрос к модели, в другом в этот момент смотрите загрузку видеокарты:
nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu --format=csv -l 1
Если во время генерации utilization.gpu близко к нулю или скачет редкими импульсами вместо стабильной загрузки — инференс на GPU не идёт, весь расчёт выполняет CPU. Это самая частая находка при внезапном замедлении: до этого модель полностью помещалась в видеопамять и считалась на GPU, а сейчас по какой-то причине скатилась на CPU-путь, который на порядки медленнее для матричных операций такого объёма.
Проверка 2 — не переполнена ли видеопамять. В той же команде смотрите memory.used относительно memory.total. Если памяти впритык или она "плавает" рывками — модель, скорее всего, не помещается целиком, и часть слоёв ушла на CPU через offloading (частичную выгрузку слоёв модели за пределы GPU). Это не авария и не ошибка — рантайм (llama.cpp, Ollama, vLLM) сознательно так делает, чтобы вообще не упасть с out-of-memory, но платит за это резким падением скорости генерации, потому что каждый forward-pass теперь дважды пересекает шину между CPU и GPU.
Проверка 3 — не конкурирует ли процесс за ту же карту. Список всех процессов, которые держат GPU прямо сейчас:
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
Если в списке два и больше PID вместо одного — видеокарта делится между несколькими нагрузками, и планировщик CUDA переключает контекст между ними, теряя на этом время. Один процесс может тихо съедать VRAM и вычислительное время, даже если он не относится к вашей основной модели.
Эти три команды закрывают диагностику примерно за минуту и почти всегда указывают прямо на причину — дальше остаётся её устранить.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть ИИ на сервереПричина 1: контекст диалога вырос и съел KV-кеш
Самый частый сценарий "было быстро — стало медленно без единого изменения в конфиге": диалог с моделью просто стал длиннее. Каждый токен истории хранится в KV-кеше (кеше ключей и внимания на каждом слое), и этот кеш растёт линейно с длиной контекста, занимая ту же видеопамять, что и сами веса модели. Если у вас накопился длинный чат — техподдержка, RAG с большим контекстом документов, агент, который сам себе дописывает историю — KV-кеш может вырасти настолько, что видеопамяти перестаёт хватать на полный набор слоёв модели, и рантайм автоматически откидывает часть слоёв на CPU. Формально это тот же offloading из проверки 2, но триггер именно длина диалога, а не сама модель.
Диагностируется просто: если замедление воспроизводится только на длинных диалогах, а на коротком новом чате всё быстро — дело в контексте. Подробнее о механике KV-кеша и о том, почему именно диалог, а не модель, "съедает" видеопамять — в статье что такое KV-кеш и почему диалог ест VRAM.
Фикс: ограничить максимальную длину контекста (num_ctx в Ollama, --max-model-len в vLLM, аналогичный параметр в llama.cpp), обрезать историю диалога перед отправкой в модель, либо — если контекст нужен целиком — считать заранее, сколько VRAM он потребует при полной длине, и держать запас памяти под него изначально, а не постфактум.
Причина 2: фоновый процесс занял видеокарту
Второй по частоте случай — кто-то (часто сам оператор) оставил запущенным лишний процесс на той же GPU: тестовый скрипт обучения, второй экземпляр модели после неудачного перезапуска, Jupyter-ноутбук с загруженной моделью, который забыли остановить, или даже видеокодирование через NVENC на той же карте. Проверка 3 выше это как раз и покажет — несколько PID вместо одного в nvidia-smi --query-compute-apps.
Отдельно стоит проверить "зависшие" процессы — те, что держат память GPU, но давно ничего не считают:
fuser -v /dev/nvidia*
ps aux | grep -i <имя_процесса>
Такое бывает, если процесс упал не полностью и оставил CUDA-контекст висеть — карта продолжает числиться занятой, хотя формальный процесс уже не отвечает.
Фикс: остановить лишний процесс (kill <PID> или через systemd, если это сервис), а на будущее — закрепить основной инференс за конкретной картой через CUDA_VISIBLE_DEVICES, если карт несколько, чтобы фоновые задачи физически не могли попасть на ту же GPU. Если несколько пользователей действительно должны делить одну карту одновременно — это отдельная тема очередей и батчинга запросов, а не борьба с "случайными" процессами; про неё подробнее в статье батчинг запросов: почему один пользователь тратит GPU.
Причина 3: обновился драйвер или библиотека — и ускорение отвалилось
Третий сценарий встречается реже, но бьёт сильнее всего, потому что не связан с самой моделью и его сложнее заподозрить: после планового обновления пакетов (apt upgrade, обновление образа Docker, обновление CUDA toolkit или драйвера NVIDIA) рантайм модели тихо перестаёт использовать аппаратное ускорение и откатывается на программный CPU-путь — при этом сервис продолжает работать и отвечать, просто на порядки медленнее, без явной ошибки в логах.
Проверить версию драйвера и совместимость с рантаймом:
nvidia-smi --query-gpu=driver_version,name --format=csv
nvcc --version 2>/dev/null || echo "CUDA toolkit не в PATH"
и сверить с тем, что реально загрузился именно GPU-билд, а не CPU-fallback. Например, для llama.cpp/Ollama в логе запуска должна быть строка про обнаруженный CUDA-девайс и число выгруженных на GPU слоёв (n_gpu_layers / "offloaded X/Y layers to GPU"); для vLLM — явное указание используемого устройства при старте. Если модель стартовала до последнего обновления пакетов, разница будет видна прямо в этих логах: старый лог показывает GPU, новый — нет.
Фикс: зафиксировать рабочую связку версий (драйвер + CUDA toolkit + версия рантайма) в отдельном файле или Dockerfile и не обновлять компоненты вслепую все разом; при откате — переустановить именно драйвер под конкретный CUDA toolkit:
apt list --installed | grep -i nvidia
apt-get install --reinstall nvidia-driver-<версия>
Точные версии и команды переустановки зависят от вашего дистрибутива и от того, какая версия CUDA нужна конкретному рантайму — ориентируйтесь на документацию используемого инструмента, а не только на этот пример.
Как быстро локализовать причину без перебора
Если время поджимает и разбираться по порядку некогда, порядок проверки такой:
| Шаг | Команда | Что смотреть | Если "да" |
|---|---|---|---|
| 1 | nvidia-smi -l 1 во время запроса | Растёт ли utilization.gpu | Если нет — GPU вообще не используется, ищите причину 3 (драйвер/рантайм) |
| 2 | nvidia-smi --query-compute-apps | Один PID или несколько | Несколько — причина 2 (чужой процесс) |
| 3 | memory.used vs memory.total | Память впритык только на длинных диалогах | Да — причина 1 (KV-кеш) |
| 4 | Лог запуска рантайма | Есть ли строка про GPU-девайс и число слоёв на GPU | Нет полного офлоада — сверяйте версию модели/квантизации против объёма VRAM |
Такая таблица закрывает диагностику за один проход почти для любого локального инференса — что на голом железе, что в Docker-контейнере.
Профилактика: мониторьте GPU и VRAM отдельно от общих метрик сервера
Главный урок такого инцидента — что его вообще пришлось разбирать руками постфактум. Стандартный мониторинг сервера (CPU, RAM, диск, сеть) в этой ситуации молчит: и CPU, и общая память, и диск могут быть в норме, пока GPU-инференс уже деградировал в десять раз. Загрузку и память видеокарты нужно снимать отдельным экспортером и смотреть отдельным графиком, а не полагаться на то, что "сервер зелёный".
Практическая связка для постоянного мониторинга:
# nvidia_gpu_exporter отдаёт метрики в формате Prometheus
docker run -d --gpus all -p 9835:9835 utkuozdemir/nvidia_gpu_exporter:latest
и дальше — Prometheus + Grafana с готовым дашбордом по nvidia_gpu_* метрикам, либо более лёгкий вариант через nvtop для разового визуального контроля в терминале. Важно завести алерт именно на связку "загрузка GPU близка к нулю при непустой очереди запросов" и на "память GPU выше N% дольше M минут" — это ловит ровно тот сценарий, который разобран выше, до того как на него пожалуются пользователи. Подробный разбор, что именно снимать и как читать эти графики, — в статье как мониторить нагрузку локальной LLM.
Если офлоад слоёв на CPU происходит регулярно, а не разово — это сигнал, что модель в текущей квантизации в принципе на грани по VRAM для вашей карты, и стоит либо взять более сжатую квантизацию, либо посчитать, сколько памяти реально требуется с учётом контекста, прежде чем упираться в этот потолок снова; ориентиры по расчёту памяти под инференс разобраны в статье пропускная способность памяти: потолок локальной LLM.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть ИИ на сервереНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Как быстро понять, CPU или GPU считает ответ, без установки дополнительных инструментов?
Запустите nvidia-smi -l 1 в отдельном терминале и отправьте запрос модели — если во время генерации загрузка GPU остаётся около нуля, считает CPU. Дополнительных пакетов ставить не нужно, nvidia-smi идёт с драйвером NVIDIA.
Offloading слоёв на CPU — это всегда плохо?
Нет, это защитный механизм, чтобы модель не падала с ошибкой нехватки памяти, если целиком не помещается в GPU. Плохо не наличие offloading как такового, а то, что он включился незаметно и неожиданно замедлил работу — важно знать, что он есть, и держать под него запас VRAM заранее, если он предсказуем.
Может ли причина быть не в GPU и не в модели, а в сети или диске?
Да, но тогда замедление обычно проявляется по-другому: задержка перед первым токеном (network/API), а не растянутая по времени сама генерация токенов. Если задержка именно в скорости генерации токен-за-токеном — почти всегда дело в вычислительном пути (GPU/CPU) или памяти, а не в сети или диске.
Нужно ли перезапускать модель после того, как нашли и устранили причину?
Обычно да — если офлоад слоёв на CPU уже включился на одном запросе, рантайм не всегда возвращает их обратно на GPU автоматически после того, как память освободилась. Проще перезапустить сервис инференса и убедиться по логу запуска, что все слои снова загружены на GPU.
Как отличить конкуренцию за GPU от нехватки видеопамяти под контекст, если оба дают похожий эффект?
По nvidia-smi --query-compute-apps: если там один PID — виноват контекст (причина 1), если несколько — конкуренция (причина 2). Это разделяет два похожих по симптому, но разных по фиксу случая за одну команду.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →