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

Qwen3.8-Flash-Next и mmap: почему tensor-read-lazy не спасает от нехватки памяти

Qwen3.8-Flash-Next может не помещаться в память даже при включенном tensor-read-lazy. Разбираем разницу между mmap и load-mode auto, влияние --ngl, --split-mode

Коротко

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

  1. 01

    Короткий ответ: tensor-read-lazy не заменяет mmap

  2. 02

    mmap, load-mode auto и ручной режим загрузки

  3. 03

    Куда уходит память при запуске Qwen3.8-Flash-Next

  4. 04

    Длинный контекст: причина, которая проявляется после загрузки

tensor-read-lazy и mmap решают разные задачи. Отложенное чтение тензоров может изменить момент и способ обращения к отдельным данным, но само по себе не гарантирует, что файл GGUF будет обслуживаться через memory-mapped доступ и что вся модель потребует меньше ОЗУ или VRAM.

При запуске Qwen3.8-Flash-Next нехватка памяти складывается из нескольких частей: квантизированных весов, данных, отправленных на GPU через --ngl, буферов CPU и GPU backend, KV-кэша, временных рабочих областей и служебных копий. Длинный контекст способен добавить нагрузку уже после успешного чтения файла модели. Поэтому строка tensor-read-lazy в конфигурации не доказывает, что причина сбоя устранена.

Для диагностики нужно отдельно сравнить load-mode auto и ручной режим mmap, зафиксировать backend, уровень offload, схему распределения по GPU и размер контекста. Каждый прогон через llama-bench должен менять один фактор, иначе влияние конкретного параметра останется неясным.

Короткий ответ: tensor-read-lazy не заменяет mmap

Что именно читает tensor-read-lazy

Название tensor-read-lazy описывает отложенную работу с тензорами при чтении модели. Загрузчик может не обрабатывать все части файла одинаково на самом первом этапе и обращаться к отдельным данным тогда, когда они понадобятся для создания нужных структур или размещения слоев.

mmap относится к другому уровню. Этот режим отображает файл в адресное пространство процесса, после чего операционная система подгружает страницы по обращениям к ним. Отображение файла не означает, что модель целиком остается на диске и не требует дополнительных ресурсов. Прочитанные страницы могут попасть в файловый кэш, а backend способен создать собственные буферы и копии.

У этих механизмов разные зоны ответственности:

  • tensor-read-lazy регулирует особенности чтения и подготовки тензоров;
  • mmap задает способ доступа к файлу через отображение памяти;
  • --ngl определяет, какая часть слоев отправляется на GPU;
  • --split-mode влияет на распределение модели при использовании нескольких GPU;
  • размер контекста и параметры batch определяют объем рабочих структур и KV-кэша.

Поэтому включение lazy-чтения не следует трактовать как универсальный режим экономии памяти. Конкретное поведение зависит от версии llama.cpp, загрузчика GGUF, операционной системы, CPU backend, GPU backend и выбранной схемы offload.

Почему модель все равно может упереться в память

Размер GGUF дает полезную начальную оценку, но не описывает пиковое потребление во время загрузки и работы. В момент запуска процесс может одновременно использовать файл модели, буферы для распакованных или преобразованных данных, память под вычисления и области для копирования между CPU и GPU.

На GPU ситуация усложняется тем, что свободная VRAM распределена по отдельным устройствам. Две видеокарты с одинаковым суммарным объемом памяти не превращаются в один большой непрерывный буфер. Часть данных может не поместиться на конкретной карте даже при наличии свободного места на соседней.

Причина сбоя определяется по моменту, когда он возникает:

  • ошибка при чтении GGUF указывает на проблему доступа к файлу, адресному пространству или системной памяти;
  • ошибка во время offload связывает сбой с распределением слоев, VRAM или backend;
  • сбой при создании контекста указывает на KV-кэш и связанные с ним буферы;
  • ошибка во время benchmark может появиться из-за batch, parallel или временных compute buffers.

mmap, load-mode auto и ручной режим загрузки

Что меняет mmap при загрузке GGUF

При mmap файл GGUF отображается в виртуальное адресное пространство процесса. Операционная система загружает страницы по мере обращения, а не обязана читать весь файл одним непрерывным блоком в начале запуска.

Это снижает необходимость в некоторых явных копиях файла, но не отменяет расход памяти. Адресное пространство процесса, файловый кэш, буферы backend и данные, отправленные на GPU, продолжают учитываться отдельно. Если слой нужно использовать на видеокарте, backend должен получить доступную ему копию или представление данных, совместимое с конкретным устройством.

При memory pressure ОС может вытеснять страницы и читать их заново. На SSD это обычно означает дополнительные операции ввода-вывода, а не бесплатную работу без задержек. При отключенном mmap процесс может загрузить данные другим способом, например выделить память под чтение файла и удерживать ее дольше. Точный результат нужно подтверждать логом и измерением RSS, а не размером команды.

Почему load-mode auto не всегда выбирает ожидаемый вариант

load-mode auto оставляет выбор загрузчику. Он может учитывать поддержку конкретного режима в сборке, тип платформы, backend и внутренние ограничения реализации. Одинаковое значение auto в двух версиях программы не гарантирует одинаковую стратегию.

Название режима в командной строке тоже не заменяет лог. При сравнении ищите сообщения о выбранном способе загрузки, размерах буферов, backend, размещении слоев и создании KV-кэша. Если сборка не выводит нужную информацию, сравнение по одной скорости benchmark будет недостаточным.

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

  • проверьте вывод llama-bench --help и документацию своей версии;
  • зафиксируйте полный текст команды;
  • сохраните первые строки лога загрузки;
  • отдельно запишите предупреждения backend и ошибки выделения памяти;
  • не переносите результат между CPU и GPU backend без повторной проверки.

Когда ручная фиксация mmap полезнее auto

Явное включение mmap полезно в диагностическом эксперименте, потому что оно делает один из факторов сравнения видимым. Базовый запуск можно повторить с теми же моделью, контекстом, --ngl, --split-mode, batch и числом GPU, изменив только режим доступа к файлу.

Если ручной mmap не меняет пик ОЗУ или VRAM, это не означает, что параметр сломан. Возможно, основную память занимают KV-кэш, GPU-offload или compute buffers. Если запуск меняется, нужно проверить, на каком этапе возникла разница и не изменились ли косвенно другие настройки.

Ручной режим дает контроль над экспериментом. Он не превращает квантизированные веса в виртуальную память без затрат и не отменяет требования к свободной VRAM.

Режим или параметрЧто контролируетЧто он не гарантирует
tensor-read-lazyОсобенности отложенной работы с тензорами при чтенииСнижение общего пика ОЗУ и VRAM
mmapОтображение файла модели в адресное пространствоОтсутствие буферов и копий backend
load-mode autoАвтоматический выбор доступной стратегии загрузкиПредсказуемый режим без проверки лога
--nglКоличество слоев, отправляемых на GPUЭкономию памяти CPU и GPU одновременно
--split-modeСпособ распределения данных между GPUОбъединение VRAM в одну общую область

Куда уходит память при запуске Qwen3.8-Flash-Next

Веса модели и квантизация: только первая часть расчета

Квантизация уменьшает размер весов в GGUF, но разные тензоры могут храниться в разных форматах. Заголовок файла и его фактическое содержимое тоже нужно проверять отдельно, особенно при сравнении низкобитных вариантов.

Размер файла помогает понять порядок требований к диску и базовую нагрузку при загрузке. Он не показывает точный объем всех представлений, которые понадобятся backend. Часть вычислений может использовать рабочие буферы с другой точностью, а часть данных может временно дублироваться при переносе на устройство.

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

  1. веса GGUF и структуры загрузчика;
  2. данные, размещенные в CPU RAM и VRAM;
  3. KV-кэш для выбранного контекста и числа последовательностей;
  4. compute buffers, служебные области и запас для операционной системы.

Практический разбор выбора квантизации для ограниченной видеопамяти приведен в статье о запуске Qwen3.8-Flash-Next на 6 ГБ VRAM. Там же полезно сопоставлять квантизацию с контекстом и offload, а не рассматривать размер файла отдельно.

--ngl: сколько слоев уходит на GPU

Параметр --ngl связывает размещение слоев с потреблением VRAM. При увеличении его значения больше вычислений и связанных с ними данных уходит на GPU. CPU RAM может разгрузиться, но свободное место на видеокарте уменьшается.

При снижении --ngl часть работы возвращается на CPU backend. Это помогает пройти этап загрузки при ограниченной VRAM, но может увеличить обмен между CPU и GPU и изменить скорость prefill и decode. Универсального значения --ngl для Qwen3.8-Flash-Next нет: результат зависит от GGUF, сборки и числа устройств.

Подбирать параметр нужно по фактической памяти каждой GPU. Суммарная VRAM служит ориентиром, но решение принимает конкретная карта, на которую приходится очередной буфер или группа слоев.

--split-mode и распределение по нескольким GPU

--split-mode задает способ распределения нагрузки между несколькими GPU. В зависимости от реализации и выбранного режима могут меняться раскладка слоев, объемы буферов и количество обменов между устройствами.

При неодинаковом объеме VRAM слабое место часто задает карта с меньшим запасом. Даже если большая часть весов распределилась равномерно, отдельные буферы или обязательные структуры могут потребовать свободное место именно на одном устройстве.

Для каждой видеокарты фиксируйте:

  • общий объем VRAM;
  • свободный объем перед запуском;
  • пик во время чтения и offload;
  • пик после создания контекста;
  • выбранную долю слоев и буферов.

Практика запуска больших моделей на двух GPU показывает, что в расчет приходится включать VRAM, RAM, NVMe и формат KV-кэша. Схемы с двумя RTX 3090 и дополнительными механизмами ускорения разобраны в материале о локальном стеке для Qwen3.8 27B.

Роль backend в фактическом расходе

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

GPU backend обычно требует свободного места под вычислительные области помимо весов и KV-кэша. CPU backend использует системную память, но это не делает запуск бесплатным: растет RSS процесса, увеличивается нагрузка на память и может появиться обращение к swap.

mmap влияет на доступ к файлу, но не стандартизирует остальные решения backend. При сравнении нужно записывать имя backend и параметры сборки. Результат на одной версии llama.cpp нельзя считать характеристикой модели во всех рантаймах.

Длинный контекст: причина, которая проявляется после загрузки

Почему успешная загрузка еще не означает успешный запуск

Запуск состоит из нескольких этапов:

  1. открытие и чтение заголовка GGUF;
  2. подготовка тензоров и структур модели;
  3. распределение слоев между CPU и GPU;
  4. создание compute buffers;
  5. инициализация контекста;
  6. выделение KV-кэша;
  7. выполнение prefill и decode в benchmark.

Модель может пройти первые четыре этапа и завершиться на создании контекста. В таком случае проблема связана не с тем, что файл не читается, а с объемом памяти, который потребовался для выбранного окна контекста, batch или числа параллельных последовательностей.

При разборе лога сопоставляйте сообщение об ошибке с мониторингом ресурсов. Смотрите ОЗУ отдельно от VRAM и отслеживайте каждую GPU. Итоговая скорость токенов не объясняет причину сбоя на этапе выделения памяти.

Как учитывать контекст при планировании конфигурации

Начинайте с небольшого значения контекста, которое поддерживает ваша задача, и записывайте его в каждой команде. После успешного запуска увеличивайте окно постепенно. Такой порядок помогает отделить проблему размещения весов от роста KV-кэша.

В таблицу эксперимента добавляйте размер контекста, batch, ubatch и parallel, если эти параметры используются вашей сборкой. Их изменение может повлиять на рабочие буферы даже при неизменном GGUF.

Не рассчитывайте конфигурацию вплотную к доступной памяти. Запас нужен для KV-кэша, временных операций, фоновых процессов и реального запроса, который может использовать больше токенов, чем короткий тест загрузки.

Влияние длинного контекста на выбор рантайма и буферов хорошо видно в сравнении llama.cpp и TensorSharp для другой MoE-модели. Методика сопоставления prefill, decode и warm cache описана в статье о сравнении TensorSharp и llama.cpp.

Как диагностировать проблему через llama-bench

Минимальный набор параметров для фиксации

Перед серией запусков составьте паспорт конфигурации. Без него два aparentemente похожих теста могут отличаться по причинам, которые не видны в заголовке результата.

  • путь к GGUF и точное имя файла;
  • вариант квантизации и размер файла;
  • версия llama.cpp и имя бинарного файла;
  • операционная система;
  • CPU backend и GPU backend;
  • число GPU и их объем VRAM;
  • --ngl;
  • --split-mode;
  • mmap, no-mmap, load-mode auto или другой доступный режим;
  • --tensor-read-lazy и его состояние;
  • размер контекста, batch, ubatch и parallel;
  • пики ОЗУ и VRAM, а не только среднее значение;
  • результат загрузки и сообщения об ошибках.

Синтаксис отдельных аргументов зависит от версии. Используйте только те имена, которые присутствуют в справке конкретной сборки. Ниже приведен шаблон сравнения, а не универсальная команда:

llama-bench -m /path/to/model.gguf -c CONTEXT -b BATCH -ub UBATCH -p PROMPT -n GENERATION --ngl N --split-mode MODE --mmap

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

Как читать лог загрузки и показатели памяти

В логе ищите четыре группы сообщений:

  1. выбранный backend и обнаруженные устройства;
  2. распределение слоев и буферов между CPU и GPU;
  3. размер и место размещения KV-кэша;
  4. ошибки выделения памяти или предупреждения о fallback.

Пик VRAM нужно снимать по каждой карте. Пик ОЗУ процесса и общий объем занятой системной памяти тоже стоит записывать раздельно. Это помогает отличить ситуацию, когда веса заняли GPU, от ситуации, когда загрузчик удерживает крупный буфер в RAM.

Скорость prefill и decode нужна для оценки производительности, но она не подтверждает выбранный режим загрузки. Два запуска могут показать близкие токены в секунду и при этом иметь разные пики памяти.

Матрица проверок: менять один фактор за раз

Начните с базового прогона на небольшом контексте и умеренном уровне --ngl. Зафиксируйте успешность загрузки, пики ресурсов и скорость.

ШагИзменяемый факторЧто фиксировать
1Базовая конфигурацияОЗУ, VRAM каждой GPU, лог, prefill и decode
2mmap против autoРежим загрузки, пик памяти, время старта
3--nglРаспределение слоев, VRAM и скорость
4--split-modeНагрузка на каждую GPU и межустройственный обмен
5КонтекстМомент создания KV-кэша и новый пик памяти
6tensor-read-lazyИзменения загрузки при неизменных остальных параметрах

Контекст лучше проверять после настройки размещения весов. Иначе рост KV-кэша может скрыть эффект от изменения mmap или --ngl.

Практический порядок настройки конфигурации

Сначала определить ограничивающий ресурс

Запишите свободную VRAM каждой GPU и доступную ОЗУ до запуска. Затем отметьте этап сбоя: чтение файла, распределение слоев, создание буферов, инициализация контекста или выполнение benchmark.

Если падает конкретная GPU, уменьшайте нагрузку на нее через --ngl или корректируйте --split-mode. Если заканчивается ОЗУ, проверьте CPU-часть модели, способ чтения GGUF, файловый кэш и размер контекста. Если сбой возникает после увеличения окна, первым подозреваемым становится KV-кэш.

Затем подобрать --ngl и --split-mode

Сначала выберите backend, который поддерживает нужные операции и устройства. После этого подберите уровень GPU-offload. Увеличение --ngl обычно экономит CPU RAM за счет VRAM, а уменьшение делает обратный обмен, снижая требования к видеопамяти ценой нагрузки на CPU и соединение между устройствами.

--split-mode проверяйте после базового запуска. Меняйте его при неизменном числе GPU и остальных параметрах, затем сравнивайте не только общий объем свободной памяти, но и самый загруженный адаптер.

tensor-read-lazy и mmap не заменяют настройку offload. Они отвечают за путь чтения и подготовки данных, тогда как --ngl и --split-mode меняют размещение вычислительной нагрузки.

Оставить запас под контекст и рабочие буферы

Не занимайте всю доступную VRAM весами. Оставьте пространство для KV-кэша, compute buffers и операций backend. Размер запаса нельзя честно назвать одной цифрой для всех систем, потому что его определяют модель, GGUF, версия llama.cpp, backend, контекст и параметры batch.

Проверяйте конфигурацию двумя отдельными сценариями: запуск с минимальным тестовым контекстом и benchmark с рабочим контекстом. Успешный первый сценарий подтверждает загрузку весов, но не доказывает, что система выдержит реальную длину запроса.

Если памяти не хватает, двигайтесь по порядку:

  1. уменьшите контекст и повторите запуск;
  2. проверьте число параллельных последовательностей и batch;
  3. снизьте --ngl;
  4. измените --split-mode при нескольких GPU;
  5. сравните ручной mmap с load-mode auto;
  6. только после этого оценивайте другую квантизацию.

Выводы: что запомнить о Qwen3.8-Flash-Next и mmap

Короткий чек-лист перед запуском

  • Проверьте версию llama.cpp и поддержку mmap, tensor-read-lazy и load-mode.
  • Зафиксируйте точный GGUF и его квантизацию.
  • Запишите свободную память каждой GPU и системную ОЗУ.
  • Укажите backend, число GPU, --ngl и --split-mode.
  • Зафиксируйте контекст, batch, ubatch и parallel.
  • Сравнивайте auto и ручной mmap при неизменных остальных параметрах.
  • Читайте лог загрузки, а не только итоговую скорость.
  • Отслеживайте пик VRAM на каждой карте и пик RSS процесса.
  • Оставляйте запас под KV-кэш и рабочие буферы.

Главная практическая мысль проста: tensor-read-lazy не равен mmap. Размер GGUF не равен пику потребления памяти. --ngl и --split-mode управляют размещением, backend меняет состав буферов, а длинный контекст способен стать отдельным ограничением после загрузки весов.

Выводы из одного запуска нельзя переносить на другую версию llama.cpp, другой backend или другой набор GPU без повторной проверки. Для Qwen3.8-Flash-Next надежнее всего работает короткая матрица экспериментов, где меняется один параметр, а лог и пики ОЗУ и VRAM сохраняются для каждого прогона.

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