Перейти к содержанию
Публикация AiManual

Почему llama.cpp использует CPU при загрузке модели в VRAM: разбор причин и способы решения

Модель загружена в VRAM, но CPU продолжает нагружаться? Разбираем причины: неполный оффлоад слоёв, подкачка страниц, операции на CPU. Практические советы по нас

Коротко

Что будет в материале

  1. 01

    Симптомы: как выглядит проблема

  2. 02

    Основные причины нагрузки на CPU

  3. 03

    Как проверить, сколько слоев реально загружено в GPU

  4. 04

    Практические способы снизить нагрузку на CPU

Симптомы: как выглядит проблема

Вы запускаете GGUF-модель через llama.cpp с флагом -ngl 99 на GPU с 24 ГБ VRAM, например NVIDIA A5000. Модель загружается, инференс идёт, но во время генерации вы замечаете: GPU загружен полностью, а CPU периодически подскакивает до высоких значений. Скорость генерации нестабильна, появляются лаги. Такое поведение сбивает с толку: если модель в VRAM, почему процессор всё ещё работает?

Это типичная ситуация для llama.cpp и аналогичных инструментов. Причины могут быть разными: от неполного оффлоада слоёв до скрытой работы операционной системы. Разберём каждую и дадим практические рекомендации, как снизить нагрузку на CPU и получить стабильную производительность.

Основные причины нагрузки на CPU

Нагрузка на CPU при загруженной в VRAM модели возникает из-за четырёх основных факторов:

  • неполное перемещение всех слоёв модели в VRAM;
  • подкачка страниц видеопамяти операционной системой;
  • выполнение на CPU операций, которые не поддерживаются GPU;
  • влияние размера контекста и настроек параллельных вычислений.

Рассмотрим каждый подробнее.

Неполный оффлоад слоев: почему -ngl 99 не всегда работает

Флаг -ngl (n-gpu-layers) указывает, сколько слоёв модели загрузить в VRAM. Значение 99 обычно означает «загрузить все слои», но фактическое количество может быть меньше. Причины:

  • Нехватка VRAM с учётом контекста и коммуникационных буферов. Даже если модель весит меньше 24 ГБ, память нужна ещё под KV-кэш, вычисления и служебные данные. При нехватке llama.cpp автоматически оставляет часть слоёв на CPU.
  • Ограничения архитектуры модели. Некоторые модели имеют слои, которые нельзя оффлоадить на GPU, например из-за нестандартных операций.

В логах запуска llama.cpp выводит строку вида llm_load_tensors: offloaded 24/28 layers to GPU. Если число меньше общего количества слоёв, часть вычислений выполняется на CPU. Для модели 27B на 24 ГБ VRAM часто оптимально указывать -ngl 24, оставляя запас под контекст. Это может дать более стабильную работу, чем попытка загрузить все слои.

Подкачка страниц видеопамяти: скрытая работа ОС

Операционная система может выгружать часть данных из VRAM в системную память, если видеопамяти не хватает. Это называется подкачкой страниц (paging). При обращении к выгруженным данным возникает задержка и нагрузка на CPU, так как данные нужно вернуть в VRAM. Подкачка может происходить даже при, казалось бы, достаточном объёме VRAM из-за фрагментации памяти или пиковых нагрузок.

Чтобы проверить, происходит ли подкачка, следите за использованием памяти GPU и CPU во время инференса. Если CPU активно работает с диском или памятью, а GPU периодически простаивает, вероятна подкачка.

Операции, которые всегда выполняются на CPU

Некоторые компоненты модели или операции не поддерживаются GPU и всегда выполняются на CPU. Это может быть:

  • отдельные слои, например нормализация или эмбеддинги, если их реализация не оптимизирована под GPU;
  • определённые типы квантования, которые требуют вычислений на CPU;
  • операции с токенами, такие как токенизация и детокенизация.

Даже при полном оффлоаде всех слоёв эти операции будут создавать периодическую нагрузку на CPU. Обычно она невелика, но может стать заметной при длинных последовательностях.

Влияние контекста и параллельных вычислений

Размер контекста напрямую влияет на объём вычислений и памяти. Большой контекст увеличивает KV-кэш, который может не поместиться в VRAM и частично обрабатываться на CPU. Также настройки параллельных вычислений, такие как количество потоков CPU (-t), могут влиять на распределение нагрузки. Если потоков слишком много, они могут конкурировать с GPU за ресурсы, вызывая задержки.

Как проверить, сколько слоев реально загружено в GPU

Самый простой способ - посмотреть вывод llama.cpp при запуске. Найдите строку с информацией об оффлоаде слоёв. Пример:

llm_load_tensors: offloaded 24/28 layers to GPU

Если числитель меньше знаменателя, часть слоёв осталась на CPU. Это основная причина нагрузки на CPU во время инференса. Также обратите внимание на строки, указывающие использование памяти, например llm_load_tensors: VRAM used: 18.2 GB. Если VRAM почти заполнена, возможна подкачка.

Практические способы снизить нагрузку на CPU

Вот конкретные шаги для оптимизации. Применяйте их по одному, проверяя эффект.

Корректировка -ngl: подбор оптимального количества слоев

Не всегда нужно загружать все слои. Начните с меньшего значения, например -ngl 20, и постепенно увеличивайте, следя за использованием VRAM и логами. Цель - найти максимальное число слоёв, при котором VRAM не переполняется и не возникает подкачки. Для модели 27B на 24 ГБ VRAM часто подходит -ngl 24. Оставляйте запас 1-2 ГБ под контекст и коммуникации.

Использование --no-mmap для ускорения загрузки

По умолчанию llama.cpp использует mmap для отображения файла модели в память. Это экономит RAM, но может вызывать подкачку страниц при обращении к модели. Если у вас достаточно оперативной памяти (например, 64 ГБ), попробуйте запустить с флагом --no-mmap. Модель будет загружена целиком в RAM, что исключит подкачку и снизит нагрузку на CPU. Однако это увеличит потребление RAM и время загрузки.

Настройка потоков CPU (-t/--threads)

Параметр -t задаёт количество потоков для вычислений на CPU. Если часть слоёв осталась на CPU, увеличение потоков может ускорить их обработку. Но слишком большое число потоков создаст конкуренцию с GPU. Начните с числа физических ядер и тестируйте. Например, для 8-ядерного процессора попробуйте -t 4, -t 6, -t 8 и сравните скорость.

Эксперименты с --batch-size

Параметр --batch-size определяет количество токенов, обрабатываемых за один шаг. Больший размер пакета увеличивает нагрузку на GPU, но может уменьшить частоту обращений к CPU. Однако он также требует больше памяти. Попробуйте значения 512, 1024, 2048 и измерьте скорость генерации. Оптимум зависит от модели и оборудования.

Заключение: баланс между CPU и GPU

Полностью исключить нагрузку на CPU в llama.cpp часто невозможно из-за архитектурных ограничений и необходимости выполнять некоторые операции на процессоре. Но её можно минимизировать. Проверьте логи, скорректируйте -ngl, используйте --no-mmap при достаточной RAM, настройте потоки и размер пакета. Экспериментируйте и мониторьте использование ресурсов. Так вы добьётесь стабильной работы и максимальной скорости на вашем оборудовании.

Дополнительные материалы по теме: как VRAM, PLE и контекст меняют скорость в llama.cpp, запуск больших LLM на 16 ГБ VRAM, как mmap помогает запускать модели на 16 ГБ VRAM, почему tensor-read-lazy не спасает от нехватки памяти.

Подписаться на канал