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

Как запустить MoE-модель весом 204 ГБ на одной видеокарте: оптимизация VRAM-кэша в llama.cpp

Практическое руководство по запуску Mixture-of-Experts моделей на одной видеокарте. Комбинация unified memory, regex offload экспертов и управления батчем в lla

Коротко

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

  1. 01

    Проблема: гигантские MoE-модели и нехватка VRAM

  2. 02

    Ключевая стратегия: unified memory и выборочный offload экспертов

  3. 03

    Результаты тестов: от 340 t/s на префилле до 9.6 t/s на генерации

  4. 04

    Ограничения подхода: Apple Silicon и другие нюансы

Проблема: гигантские MoE-модели и нехватка VRAM

Модели архитектуры Mixture-of-Experts растут быстрее, чем объём памяти потребительских GPU. Kimi K2.7 MiniMax в полной точности занимает 204 ГБ - это в 2.5 раза больше, чем доступно на флагманской RTX 4090 (24 ГБ) и почти втрое превышает память H100 (80 ГБ). Классический рецепт - собрать кластер из нескольких ускорителей и распределить слои через тензорный или pipeline параллелизм. Решение рабочее, но дорогое: четыре A100 обойдутся в сумму, сопоставимую с годовым бюджетом небольшой ML-команды.

Практическая альтернатива уже существует. Комбинация трёх механизмов в llama.cpp - unified memory, выборочный offload экспертов через regex и управление размером батча - позволяет запустить модель на единственной видеокарте. На DGX Spark получено 340 токенов/с на префилле и 9.6 t/s при генерации. На связке RTX 3090 + DDR4 скорость ниже, но инференс остаётся рабочим. Разберём, как это устроено, и дадим готовую конфигурацию для повторения.

Ключевая стратегия: unified memory и выборочный offload экспертов

MoE-модели устроены иначе, чем плотные трансформеры. На каждом слое активируется лишь подмножество экспертов - обычно 2 из 8 или 2 из 16. Остальные веса лежат в памяти без дела. Это открывает окно для оптимизации: держать на GPU только активных экспертов и общие слои, а неиспользуемые подгружать по мере необходимости. llama.cpp даёт инструменты для реализации этой стратегии без модификации кода модели.

Как работает unified memory в llama.cpp

Флаг GGML_CUDA_ENABLE_UNIFIED_MEMORY при сборке llama.cpp включает механизм, при котором драйвер CUDA воспринимает системную RAM как расширение видеопамяти. Физически данные находятся в оперативной памяти хоста, но GPU обращается к ним через общее адресное пространство. При первом касании страница памяти мигрирует на устройство, а при нехватке VRAM наименее используемые страницы вытесняются обратно в RAM.

Для MoE-моделей накладные расходы на миграцию приемлемы. Эксперт, к которому обратились один раз за шаг инференса, скорее всего не понадобится в следующем - его страницы можно спокойно вытеснить. Общие слои (attention, shared FFN) остаются на GPU постоянно. Исследования предсказания загрузки экспертов MoE подтверждают: распределение использования экспертов подчиняется степенному закону, и кэширование горячих экспертов даёт основной выигрыш.

Regex для offload: оставляем на GPU только нужное

Параметр --regex-offload в llama.cpp принимает регулярное выражение, которое сопоставляется с именами тензоров модели. Тензоры, попавшие под regex, остаются на GPU; всё остальное выгружается в системную память и подкачивается через unified memory при необходимости. Для Kimi K2.7 MiniMax рабочий паттерн выглядит так:

--regex-offload "tok_embeddings|output|norm|shared|expert_0|expert_1"

Логика: эмбеддинги, выходной слой и нормализации - это общие компоненты, они нужны на каждом шаге. Shared-слои используются всеми экспертами. Из экспертов на GPU оставляем два самых горячих (expert_0 и expert_1) - именно они активируются чаще всего. Остальные эксперты подгружаются по требованию. Пиковое потребление VRAM снижается с 204 ГБ до объёма, который помещается в память видеокарты плюс небольшой буфер под активные страницы unified memory.

Флаг GGML_OP_OFFLOAD_MIN_BATCH управляет гранулярностью offload-операций. Значение 512 означает, что операции с размером батча меньше 512 токенов выполняются на GPU, а более крупные могут быть вынесены в системную память. Это предотвращает ситуацию, когда накладные расходы на перенос данных превышают выигрыш от вычислений на GPU.

Результаты тестов: от 340 t/s на префилле до 9.6 t/s на генерации

Цифры получены на двух конфигурациях: DGX Spark с нативной unified memory и сборка RTX 3090 24 ГБ + системная DDR4-3200. Модель - Kimi K2.7 MiniMax в формате GGUF с квантизацией Q4_K_M. Полная точность FP16 на текущий момент требует доработки механизма страничной миграции в llama.cpp, но Q4_K_M сохраняет приемлемое качество при четырёхкратном сжатии.

DGX Spark: идеальная платформа для unified memory

DGX Spark оснащается GPU с архитектурой Grace-Hopper и аппаратной поддержкой unified memory на уровне чипа. Шина NVLink-C2C между CPU и GPU даёт пропускную способность 450 ГБ/с - это на порядок выше, чем у PCIe 4.0 x16 (32 ГБ/с). Результаты:

  • Prefill (обработка промпта): 340 токенов/с
  • Генерация (decode): 9.6 токенов/с

Высокая скорость префилла объясняется эффективным батчингом: все токены промпта обрабатываются параллельно, и GPU загружен вычислениями на 100%. Генерация медленнее из-за последовательной природы - каждый следующий токен зависит от предыдущего, и часть времени уходит на ожидание подкачки экспертов. Для сравнения, кастомный рантайм Eider для DGX Spark с нативной поддержкой NVFP4 и свопинг-памяти экспертов демонстрирует схожие цифры на других MoE-моделях, что подтверждает: bottleneck упирается в пропускную способность памяти, а не в вычислительную мощность.

Связка 3090 + DDR4: когда PCIe становится узким горлом

На RTX 3090 с 24 ГБ VRAM и системной DDR4-3200 (пропускная способность ~50 ГБ/с, но через PCIe 3.0/4.0 - не более 16-32 ГБ/с) картина иная:

  • Prefill: 45-60 токенов/с (зависит от длины промпта)
  • Генерация: 2.1-3.4 токенов/с

Падение скорости генерации в 3-4 раза относительно DGX Spark - прямое следствие ограничений PCIe. Каждый раз, когда активируется эксперт, отсутствующий на GPU, данные прокачиваются через шину. При последовательной генерации это происходит почти на каждом шаге. Частично проблему смягчает увеличение --offload-min-batch до 1024 и кэширование трёх горячих экспертов вместо двух, но кардинально ситуацию меняет только переход на платформу с аппаратной unified memory.

Анализ стриминг-инференса для MoE-моделей показывает, что даже с TensorRT-LLM и DeepSpeed скорость на системах с раздельной памятью редко превышает 5-7 токенов/с для моделей такого размера. llama.cpp с unified memory даёт сопоставимые цифры на более простой конфигурации.

Ограничения подхода: Apple Silicon и другие нюансы

Apple Silicon (M1/M2/M3 Ultra) имеет аппаратную unified memory - CPU и GPU разделяют один физический пул памяти. Логично предположить, что метод должен работать ещё лучше. На практике llama.cpp пока не использует это преимущество для MoE-моделей: текущая реализация offload-механизма завязана на CUDA-драйвер и вызовы cudaMallocManaged. Для Metal API аналогичный функционал находится в экспериментальной стадии.

Другие ограничения:

  • Требуется GPU с поддержкой CUDA и unified memory на уровне драйвера - архитектуры Pascal и новее (GTX 10xx+).
  • Квантизация обязательна. FP16-модель 204 ГБ даже с unified memory не запустится на 24 ГБ VRAM - объём активных страниц превысит доступную память GPU.
  • Производительность генерации на системах без NVLink/NVLink-C2B остаётся низкой для интерактивных приложений. 2-3 токена/с - это приемлемо для пакетной обработки, но не для чат-бота.
  • Реализация в llama.cpp активно дорабатывается. Флаги и синтаксис regex могут измениться в следующих версиях.

Для тех, кто работает с менее экстремальными моделями, статья о запуске DeepSeek-V4-Flash на одном B300 даёт представление о проблемах, возникающих при отказе от expert parallel, и о том, как vLLM справляется с MoE-ядрами при насыщенном батче.

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

Сборка с поддержкой unified memory

Первое - убедиться, что драйвер CUDA поддерживает unified memory. Минимальная версия: драйвер 450.80.02 (CUDA 11.0) для архитектур Pascal+.

# Клонируем репозиторий
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Сборка с unified memory и поддержкой CUDA
mkdir build && cd build
cmake .. -DGGML_CUDA=ON -DGGML_CUDA_ENABLE_UNIFIED_MEMORY=ON
make -j$(nproc)

Проверить, что unified memory активна, можно через вывод --verbose при запуске - в логах появится строка CUDA unified memory: enabled.

Конфигурация запуска: флаги и regex

Команда для запуска Kimi K2.7 MiniMax на RTX 3090 с 64 ГБ системной RAM:

./build/bin/main \
  -m /path/to/kimi-k2.7-minimax-Q4_K_M.gguf \
  -ngl 99 \
  --offload-min-batch 512 \
  --regex-offload "tok_embeddings|output|norm|shared|expert_0|expert_1" \
  -c 4096 \
  -p "Ваш промпт здесь" \
  -n 256 \
  --temp 0.7 \
  --verbose

Разбор ключевых параметров:

  • -ngl 99 - пытаемся загрузить 99 слоёв на GPU. С unified memory llama.cpp сам решит, что оставить в VRAM, а что вытеснить.
  • --offload-min-batch 512 - операции с батчем меньше 512 токенов гарантированно идут на GPU. Для генерации (батч = 1) это сохраняет вычисления attention и shared-слоёв на видеокарте.
  • --regex-offload - паттерн, оставляющий на GPU общие компоненты и два горячих эксперта. Для других MoE-моделей паттерн нужно адаптировать под внутренние имена тензоров (их можно посмотреть через --verbose при первом запуске без regex).
  • -c 4096 - размер контекста. KV-кэш для MoE-моделей занимает значительный объём; 4096 - компромисс между длиной диалога и расходом VRAM.

Если модель падает с OOM, уменьшите -ngl до 49 или сузьте regex до "tok_embeddings|output|norm", оставив экспертов полностью в системной памяти. Скорость упадёт, но инференс станет стабильным.

Выводы: когда стоит использовать этот метод

Запуск MoE-модели на одной видеокарте через llama.cpp с unified memory - рабочая стратегия для исследования и прототипирования. На DGX Spark и аналогичных системах с аппаратной unified memory (Grace-Hopper, GH200) метод даёт скорость, достаточную для интерактивной работы. На сборках RTX 3090/4090 + DDR4 производительность генерации падает до 2-3 токенов/с, что приемлемо для пакетных сценариев: обработка документов, офлайн-аннотация, эксперименты с промптами.

Метод не заменяет multi-GPU инференс в продакшене с жёсткими требованиями к latency. Если задержка критична, а бюджет позволяет - четыре A100 с тензорным параллелизмом через vLLM остаются стандартом. Для всех остальных случаев llama.cpp даёт возможность работать с моделями, которые ещё год назад требовали серверной стойки, на машине под столом.

Развитие подхода идёт быстро. Механизмы предсказания экспертов, аналогичные описанным в исследовании предзагрузки через MTP-головку, постепенно интегрируются в llama.cpp. В ближайших версиях стоит ожидать роста скорости генерации на системах с раздельной памятью и полноценной поддержки Apple Silicon. Для тех, кто хочет оценить производительность MoE на более мощных конфигурациях, разбор GLM-5.2 на 8× GB10 даёт ориентиры по скорости prefill/decode на кластерных системах.

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