Проблема может быть не в модели, а во второй копии её весов

Исследователи описали «налог на загрузку» открытых моделей: операционная система уже держит файлы весов в page cache, доступном ускорителю на системах с общей или когерентной памятью, но фреймворк создаёт собственное представление тех же данных. Рядом оказываются отображённый файл и дополнительная копия. В пограничном режиме, когда одна версия помещается в память, а две уже конкурируют за неё, локальная LLM замедляется или начинает вытеснять страницы.

Работа The Ingestion Tax: Adopting File-Backed Weights in Tensor Frameworks опубликована 12 августа 2026 года. Авторы предлагают не копировать веса в отдельное хранилище фреймворка, а принять отображённые страницы как обычные тензоры через DLPack. На испытаниях это сократило время до первого токена для чекпойнта объёмом 65 ГБ в 6,4 раза, сохранило скорость Qwen2.5-72B на уровне заранее загруженной копии и уменьшило память llama.cpp на AMD APU почти вдвое.

Но это не универсальный «ускоритель любой LLM». На дискретной видеокарте через PCIe тот же подход оказался в 39 раз медленнее: каждый доступ к весам шёл через шину вместо локальной VRAM. Практический вывод для бизнеса — выбирать способ загрузки по топологии памяти и измерениям всего рабочего набора, а не по названию API или одному тесту.

Что такое налог на загрузку

Обычная загрузка модели проходит примерно так:

1. ОС читает файлы чекпойнта и помещает страницы в файловый кэш.
2. PyTorch, MLX или другой фреймворк создаёт собственный буфер.
3. Веса копируются или преобразуются в этот буфер.
4. Исходные страницы остаются в page cache, пока системе хватает памяти.

На сервере с дискретным GPU копирование в быструю VRAM обычно оправдано: последующие токены многократно используют локальные данные. На компьютере с unified memory или на системе с когерентной связью CPU–GPU отображённые страницы уже находятся в домене, который ускоритель может читать. Вторая физическая копия может не дать более быстрого размещения, зато занимает память и добавляет передачу данных.

Особенно заметен режим «одна копия помещается, две — нет». Формально модель соответствует объёму RAM, но после создания буфера фреймворка начинается сжатие памяти, swap или вытеснение полезных страниц. Поэтому проверка «размер файла меньше установленной памяти» недостаточна.

Как устроено file-backed adoption

Предложенный механизм отображает каждый тензор через `MAP_SHARED`, создаёт GPU-буфер без копирования и экспортирует его в DLPack. PyTorch или MLX импортирует капсулу как обычное хранилище. Файловые страницы остаются чистыми, могут совместно использоваться процессами и при давлении на память освобождаются без записи обратно.

Авторы подчёркивают три обязательных условия:

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

Одного zero-copy недостаточно. Первая реализация авторов убрала копирование, но из-за синхронизации работала в 2,3 раза медленнее штатного пути. Ускорение появилось только после выполнения всего контракта. Это важное предостережение: функция `from_dlpack` или memory mapping сама по себе не доказывает, что инференс стал быстрее.

Какие результаты получили авторы

Для Qwen2.5-72B int8 на машине с общей памятью file-backed вариант показал 7,14 токена в секунду. Заранее загруженная резидентная копия дала 7,23 токена в секунду, а повторное копирование при использовании — 0,94 токена в секунду. То есть отображённые веса сохранили скорость сильного резидентного варианта, но остались разделяемыми и освобождаемыми страницами.

У Qwen2.5-32B чекпойнт объёмом 65 ГБ достиг первого токена за 6,97 секунды вместо 37,95 секунды у штатного загрузчика. Авторы отдельно отмечают: большую часть разницы дала не только смена владельца памяти, но и устранение разбора шардов и перекладки данных. Нельзя приписывать все 6,4 раза одному memory mapping.

В экспериментальной конфигурации Kimi K3 плотная часть обработки весов сократилась с 2,62 до 0,35 секунды на токен, а полный токен — с 4,14 до 1,87 секунды. Потоковая загрузка маршрутизируемых экспертов с накопителя осталась отдельным узким местом и этой работой не устранялась.

На AMD Ryzen 7 9700X APU интеграция с Vulkan увеличила скорость llama.cpp с 2,82 до 3,42 токена в секунду, а пиковый рабочий набор уменьшился с 8,13 до 4,20 ГиБ. На NVIDIA GH200 отображённые страницы и перекрывающееся копирование для набора больше HBM оказались в пределах 5% друг от друга.

Обратный результат получен на RTX 5070 Ti: чтение отображённых весов через PCIe снизило скорость с 146,9 до 3,79 токена в секунду. Экономия памяти здесь ничего не компенсировала. Топология определяет путь байтов.

Где метод уместен

Общая память CPU и GPU

Apple Silicon и некоторые APU используют общий пул. Если фреймворк создаёт вторую копию весов в том же DRAM, file-backed хранение может освободить значительную долю памяти и ускорить старт. Нужны поддержка драйвера, корректный импорт внешней памяти и проверка полного рабочего набора.

Когерентная связь CPU–GPU

На системах вроде GH200 ускоритель может читать память CPU через высокоскоростную когерентную связь. Отображение становится вариантом размещения, когда веса или активный набор не помещаются в HBM. Выбор зависит от реальной пропускной способности связи и способности перекрывать передачу вычислениями.

Дискретная видеокарта через PCIe

Если модель помещается в VRAM, копирование один раз и дальнейшее локальное чтение почти всегда логичнее. File-backed чтение каждого веса через PCIe превращает экономию памяти в провал производительности. Для такой машины сначала измеряют резидентный режим, квантование и разбиение по нескольким GPU.

CPU без ускорителя

Memory mapping давно используется локальными рантаймами, но эффект зависит от формата файла, кэша ОС, NUMA и паттерна чтения. Новая работа важна прежде всего тем, что переносит отображённые страницы внутрь тензорных фреймворков без лишней копии.

Что измерить на существующем сервере

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

  • размер файлов модели и фактический resident set процесса;
  • объём page cache до и после загрузки;
  • анонимную память, swap и сжатие;
  • число физических представлений весов;
  • время холодного старта и время до первого токена после прогрева;
  • токены в секунду при одном и нескольких запросах;
  • пропускную способность CPU–GPU и наличие PCIe на пути;
  • поведение при двух процессах, если нужны разные сервисы или версии;
  • долю времени на чтение весов, преобразование формата и вычисления.

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

Экономика для малого бизнеса

Предположим, локальный чекпойнт занимает 65 ГБ. Штатный загрузчик создаёт ещё одну версию примерно того же порядка, а ОС и активации требуют дополнительного запаса. Сервер на 128 ГБ оказывается у границы: два процесса или длинный контекст могут включить swap. Это модельный пример, а не универсальная формула.

Если file-backed путь действительно оставляет одну разделяемую копию, можно отложить переход на более дорогую конфигурацию или запустить два изолированных процесса поверх одного набора страниц. Экономия возникает не из «бесплатной памяти», а из устранения дублирования.

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

Пилот на пять дней

**День 1.** Зафиксируйте модель, квантование, версии ОС, драйвера и рантайма. Снимите cold start, TTFT, токены в секунду и пиковую память.

**День 2.** Определите топологию: unified memory, когерентная связь или PCIe. Измерьте пропускную способность на полном рабочем наборе, а не на маленьком буфере.

**День 3.** Сравните штатную загрузку, memory-mapped вариант и резидентную копию, если она помещается. Промпт, число токенов и ядра должны быть одинаковыми.

**День 4.** Повторите тест с двумя процессами и под давлением памяти. Проверьте ответы побитово или на устойчивом наборе эталонов.

**День 5.** Рассчитайте стоимость принятого ответа и инженерного сопровождения. Внедряйте оптимизацию только если она выигрывает в целевом режиме и имеет безопасный откат на штатный загрузчик.

Работа остаётся свежим препринтом, а результаты привязаны к конкретным машинам, форматам и экспериментальному коду. Это повод провести измерение, а не обещать ускорение заранее. Управленческий вывод прост: прежде чем покупать память, проверьте, не хранит ли ваш стек одну и ту же модель дважды. Внутрик уже принёс вторую стопку ящиков; хороший профилировщик успеет остановить его у двери.