Экспериментальный O(1)-режим CMF заменяет растущий KV-кэш в 16 full-attention слоях Qwen3.8-27B фиксированным состоянием 44,1 МиБ. На длине 64K это сопоставляется с 4 ГиБ FP16 KV-кэша, но ценой приближённого восстановления дальнего контекста.
В CMF можно включить экспериментальный режим O(1). Он заменяет KV-кэш в полных attention-слоях компактным состоянием фиксированного размера: недавние токены сохраняются точно, начало последовательности — в постоянном наборе sink-токенов, а дальняя часть представляется набором опорных представлений. Это позволяет запускать конвертированную Qwen3.8-27B Q4TP на карте класса от 16 ГБ и не увеличивать размер этой части состояния вместе с длиной контекста.
Цена известна заранее: текущая модель обучалась для обычного внимания, поэтому перенос является приближением. Дальние факты могут восстанавливаться хуже, а первый токен длинного запроса получается медленнее. O(1) здесь означает постоянный размер состояния при фиксированных параметрах; время обработки самого входа всё равно зависит от числа токенов.
Что именно запускаем
Qwen3.8-27B — гибридная модель из 64 слоёв: 48 слоёв Gated DeltaNet с уже рекуррентным состоянием и 16 слоёв полного attention. CMF-файл qwen38-27b-q4tp.cmf содержит 4-битные веса в формате Q4TP. Его размер — 14 271 151 208 байт, то есть около 14,3 GB в десятичной записи.
Готовый файл опубликован в репозитории модели на Hugging Face.
Флаг --o1 можно передать при запуске уже готового файла. Конвертация с cortiq convert --o1 тоже поддерживается, но записывает только подсказку в заголовок: веса остаются неизменными, а режим выбирается рантаймом. Поэтому для этой демонстрации не требуется заново конвертировать чекпойнт и не требуется дообучение.
Идея на бытовом уровне
Представьте магазин с тысячами покупок. Можно записывать каждую покупку отдельной строкой — тогда журнал постоянно растёт. А можно одновременно обновлять сводную таблицу: количество продаж, выручку, число заказов. Каждая покупка влияет на показатели, но количество ячеек в таблице остаётся прежним. В CMF используется похожий принцип накопления. Недавний текст сохраняется подробно, а более ранний контекст участвует в обновлении компактного числового состояния. При ответе модель использует обе части: свежие подробности и накопленную информацию о предыдущем разговоре. Новый текст обновляет уже выделенную память, поэтому для каждого следующего фрагмента не требуется увеличивать хранилище всей истории. Именно это даёт постоянный объём состояния O(1). Пример с магазином объясняет принцип; вычисления внимания в CMF значительно сложнее обычных сумм и счётчиков.
Сколько памяти занимает контекст
Ниже сравнивается именно память состояния полного attention, без весов и остальных буферов. Для Ollama взят документированный FP16 KV-кэш по умолчанию; квантизация весов Q4 сама по себе не делает KV-кэш 4-битным. Источник настройки KV-кэша — документация Ollama.
Длина контекста | Ollama: FP16 KV-кэш | CMF O(1): состояние attention | Отношение |
|---|---|---|---|
2 048 | 128 МиБ | 44,1 МиБ | 2,9× |
4 096 | 256 МиБ | 44,1 МиБ | 5,8× |
8 192 | 512 МиБ | 44,1 МиБ | 11,6× |
16 384 | 1 ГиБ | 44,1 МиБ | 23,2× |
32 768 | 2 ГиБ | 44,1 МиБ | 46,4× |
65 536 | 4 ГиБ* | 44,1 МиБ* | 92,9× |
131 072 | 8 ГиБ* | 44,1 МиБ* | 185,8× |
262 144 | 16 ГиБ* | 44,1 МиБ* | 371,6× |
1 МиБ = 2^20 байт, 1 ГиБ = 2^30 байт. Длина — общее число удерживаемых токенов, включая вход и уже сгенерированный ответ. Столбец O(1) — фактический счётчик 46 236 672 байта (44,0947 МиБ) для m=32, окна 128, четырёх sink-токенов и калибровочного префикса 256 токенов. Это одна GPU-копия состояния внимания, а не размер всей модели.
Расчёт растущей части для Qwen3.8-27B выглядит так:
16 полных слоёв × 4 KV-головы × 256 × 2 (K и V) × 2 байта = 65 536 байт на токен
48 слоёв GDN уже используют собственное рекуррентное состояние и не входят в растущую часть таблицы. Кроме него остаются веса, рабочие буферы, драйверные выделения, хостовая копия состояния и временная память префилла. Поэтому 44,1 МиБ нельзя читать как «вся модель занимает 44 МиБ».
Если точный обычный KV хранить в f32, его payload будет вдвое больше: 131 072 байта на токен. В таблице намеренно показан FP16-вариант Ollama, чтобы сравнение соответствовало документированному значению по умолчанию.
Строки O(1) для 8k–32k совпадают с завершёнными измерениями. Строки со звёздочкой — экстраполяция постоянного состояния и формульный расчёт KV-кэша, а не подтверждение запуска модели на 256k и не гарантия качества на такой длине. Ollama в этой таблице также не прогонялся на каждой длине.
Если после загрузки весов и прочих буферов на карте осталось 4 ГиБ, один только FP16 KV-кэш этой модели израсходует такой бюджет примерно на 64k токенов. O(1) снимает линейный рост именно этого компонента; пределы модели, время обработки входа и качество сжатой памяти остаются.
Установка на Linux с Vulkan
Ниже приведён рецепт для NVIDIA Vulkan. На проверенном стенде это RTX PRO 4000 Blackwell с 24 467 МиБ доступной VRAM и лимитом мощности 145 Вт. Для хост-системы стоит иметь 64 ГБ оперативной памяти и запас на диске под файл модели, временные данные и сборку. Универсальный минимальный объём для всех карт здесь не объявляется: он зависит от драйвера, рабочих буферов и выбранного файла.
До установки пользовательских Vulkan-пакетов установите совместимый драйвер NVIDIA официальным способом для своего дистрибутива. libvulkan1, libglvnd0 и vulkan-tools — это загрузчик и инструменты; они сами по себе не устанавливают ядровой драйвер. Быстрая проверка — nvidia-smi, затем vulkaninfo --summary.
Вариант 1: готовый бинарник релиза
Имена архивов соответствуют workflow сборки релизов. Для Linux x86_64 используется cortiq-x86_64-unknown-linux-gnu.tar.gz:
mkdir -p cmf-qwen38 cd cmf-qwen38 curl -LO https://github.com/infosave2007/cmf/releases/download/v0.6.6/cortiq-x86_64-unknown-linux-gnu.tar.gz curl -LO https://github.com/infosave2007/cmf/releases/download/v0.6.6/cortiq-x86_64-unknown-linux-gnu.tar.gz.sha256 sha256sum -c cortiq-x86_64-unknown-linux-gnu.tar.gz.sha256 tar -xzf cortiq-x86_64-unknown-linux-gnu.tar.gz mkdir -p "$HOME/.local/bin" install -m 0755 cortiq "$HOME/.local/bin/cortiq" export PATH="$HOME/.local/bin:$PATH" cortiq --version
Ожидаемый вывод последней команды для этого материала — cortiq 0.6.6. Ссылки на архивы и статус сборки находятся на странице релиза v0.6.6.
Вариант 2: установка через Cargo
Используйте актуальный stable toolchain. Указанный в проекте rust-version = 1.85 — нижняя граница манифеста, а требования зафиксированных транзитивных зависимостей могут потребовать более свежий stable:
rustup toolchain install stable cargo +stable install cortiq-cli --version 0.6.6 --locked cortiq --version
Обычная установка cortiq-cli включает GPU-функции. CPU-only вариант собирается отдельно с --no-default-features; для описанного Vulkan-профиля этот вариант не нужен.
Загрузить и проверить модель
Установите CLI Hugging Face (hf) по официальной инструкции. Компактный изолированный вариант через Python выглядит так:
python3 -m venv .venv . .venv/bin/activate python -m pip install --upgrade huggingface_hub hf --help
После этого скачайте ровно один файл из закреплённой ревизии:
hf download infosave/Qwen3.8-27B-cmf qwen38-27b-q4tp.cmf \ --revision 31699079edefe40c9d1223158597fbfa37c234ce \ --local-dir . printf '%s %s\n' \ 'eec6a35934f45e16cfcff3c0c223846aa498cb8fb419e675e3b73b708c00655c' \ qwen38-27b-q4tp.cmf | sha256sum -c - cortiq verify qwen38-27b-q4tp.cmf cortiq info qwen38-27b-q4tp.cmf
Проверка SHA-256 защищает от неполной загрузки или подмены файла. cortiq verify открывает CMF-контейнер, проверяет envelope, границы секций, каталог тензоров и хэши каждого тензора; при наличии файла .sig дополнительно проверяется detached Ed25519-подпись, а отсутствие sidecar не считается ошибкой. Это отдельные проверки целостности файла и подписи. Команда info читает контейнер и печатает архитектуру, слои, головы и квантование; инференс она не запускает.
Настроить GPU и запустить O(1)
Сначала проверьте, какой адаптер видит Vulkan:
sudo apt update sudo apt install libvulkan1 libglvnd0 libegl1 libgl1 libglx0 vulkan-tools export XDG_RUNTIME_DIR=/tmp cortiq gpu
cortiq gpu перечисляет адаптеры, выбранное устройство и его лимиты; при наличии адаптера дополнительно измеряет пустой roundtrip до устройства. Модель он не загружает и не запускает. Если в списке несколько карт, выберите номер из вывода cortiq gpu. Вместо номера можно использовать подстроку имени, например CMF_GPU_ADAPTER=5090.
Для воспроизводимого запуска Qwen3.8-27B на Vulkan используется следующий профиль. Это набор параметров конкретного измеренного стенда, а не обязательный набор для каждой видеокарты:
export CMF_GPU=1 export WGPU_BACKEND=vulkan export XDG_RUNTIME_DIR=/tmp export CMF_GPU_ADAPTER=0 export CMF_GPU_PROBE=0 export CMF_BATCH_K=128 export CMF_BATCH_COOP=1 export CMF_GRAPH_BGCACHE=0 export CMF_GRAPH_SPLIT=16 export CMF_PASSFUSE=1 export CMF_GPU_GROUP=1 export CMF_GRAPH_SPEC=0 export CMF_MTP=0 export CMF_VERIFY_I8=0 export CMF_O1_GPU=1 export CMF_O1_PREFILL=256 export CMF_KV=f32
Что означают существенные настройки:
Настройка | Значение в профиле | Назначение и базовое поведение |
|---|---|---|
|
| запросить GPU; без переменной используется CPU-путь |
|
| выбрать Vulkan для wgpu |
|
| адаптер по индексу; значение по умолчанию — 0 |
|
| доверять GPU без стартового чередования CPU/GPU; по умолчанию проба включена |
|
| батч префилла; по умолчанию 0, то есть последовательный путь |
|
| включить cooperative-matrix путь широкого батча; по умолчанию выключен |
|
| число частей графа; в текущем рантайме значение по умолчанию 16 |
|
| объединять независимые проекции в один pass; по умолчанию включено |
|
| сливать соседние последовательные этапы графа; по умолчанию включено |
|
| выключить speculative decode для сопоставления с O(1); без явного |
|
| выбрать f32-проверку speculative-пути; по умолчанию используется int8, но в этом профиле speculative decode выключен |
|
| выполнять O(1) attention на GPU; GPU-ветка O(1) opt-in |
|
| точный калибровочный префикс перед запечатыванием; без переменной префикс следует обычной полной обработке запроса |
|
| точный KV-тип для этого O(1)-пути; по умолчанию также f32 |
CMF_GRAPH_BGCACHE=0 в профиле оставляет кэш bind-group выключенным, чтобы результат совпадал с принятой серией. CMF_GRAPH_SPLIT, CMF_GPU_GROUP и CMF_PASSFUSE влияют на граф и могут дать другое время на другой карте. Если вы меняете их, заново измерьте свой стенд.
После загрузки состояния запустите одношаговый ответ:
cortiq run qwen38-27b-q4tp.cmf \ --o1 all --o1-m 32 --o1-window 128 --o1-sink 4 \ --temperature 0 --seed 42 --rep-penalty 1.0 \ --no-think --max-tokens 256 \ --prompt 'Объясни простыми словами, зачем языковой модели фиксированная память контекста.'
--o1 all означает «перевести доступные полные attention-слои», то есть в этой гибридной модели 16 слоёв. 48 GDN-слоёв продолжают использовать свой рекуррентный механизм. --o1-m 32 задаёт число landmarks, --o1-window 128 — размер точного окна, --o1-sink 4 — число точных ключей в начале последовательности.
--no-think просит шаблон чата сразу вывести ответ без блока рассуждений. Он несовместим с --raw: последний подаёт строку модели без chat template. Если нужен raw-режим, уберите --no-think и добавьте --raw отдельным запуском. --temperature 0, --seed 42 и --rep-penalty 1.0 фиксируют воспроизводимый greedy-профиль без штрафа повторов.
После ответа run печатает количество токенов, время до первого токена, скорость последующего декода, общую скорость и причину завершения. Это позволяет отделить медленный префилл от steady-state decode, не измеряя вывод терминала вручную.
Для обычного режима на том же файле используйте явное отключение O(1):
CMF_O1_GPU=0 cortiq run qwen38-27b-q4tp.cmf \ --o1 off \ --temperature 0 --seed 42 --rep-penalty 1.0 \ --no-think --max-tokens 256 \ --prompt 'Объясни простыми словами, зачем языковой модели фиксированная память контекста.'
Для честного A/B сравнения оставьте одинаковыми файл, промпт, число выходных токенов, seed и backend. O(1) и speculative decode в текущем runtime используют разные жизненные циклы состояния, поэтому измеренный профиль выше намеренно задаёт CMF_MTP=0.
Серверный режим
Одноразовый run --prompt завершает процесс после ответа. Если приложение само передаёт историю диалога, можно поднять локальный HTTP-сервер и отправлять её в OpenAI-совместимый endpoint:
cortiq serve qwen38-27b-q4tp.cmf \ --host 127.0.0.1 --port 8080 \ --o1 all --o1-m 32 --o1-window 128 --o1-sink 4
Доступны /v1/models, /v1/chat/completions и /v1/completions. /v1/models возвращает один рекламируемый идентификатор вида <arch>-cortiq; поле model в запросе обязательно и возвращается в ответе, а файл выбирается позиционным аргументом serve. Пример запроса к чату:
curl http://127.0.0.1:8080/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{"model":"Qwen3.8-27B-cmf","messages":[{"role":"user","content":"Что такое O(1) память?"}],"temperature":0,"max_tokens":256,"enable_thinking":false}'
enable_thinking:false передаётся в chat template и подавляет <think>; верхнеуровневое поле имеет приоритет над chat_template_kwargs.enable_thinking. Для файла с task masks можно добавить "cortiq":{"task":"имя-маски"}: неизвестная маска вернёт 404, без поля используется выбор сервера из --task (по умолчанию general). Историю нужно передавать в messages на каждом запросе, как и в обычном OpenAI-совместимом клиенте. CMF не загружается в Ollama автоматически: для .cmf используйте cortiq, а Ollama оставьте отдельным baseline с его собственной моделью.
Как устроен переход на O(1)
При обычном префилле рантайм строит точное состояние для начальной части запроса. Переменная CMF_O1_PREFILL=256 ограничивает калибровочный префикс: эти токены проходят точное внимание, по их запросам строится скелет, затем полные временные K/V для O(1)-слоёв освобождаются. Остаток длинного промпта и последующая генерация идут через потоковый оператор.
Слово «переход» важно: на длинном запросе первый токен не становится мгновенным. Точная подготовка префикса и построение опорных представлений занимают время, зато после запечатывания размер состояния не растёт от каждого следующего токена. При коротком запросе, который помещается в sink + window с небольшим запасом, скелет может вообще не включаться — это нормальный точный путь.
Внутри O(1) есть три части:
постоянные sink-ключи для первых четырёх позиций;
кольцо последних 128 ключей и значений, которые читаются точно;
опорные представления и накопители дальней части, ограниченные бюджетом
m=32.
Параметры по умолчанию для этого runtime — m=32, window=128, sink=4. Для wgpu-пути проверка геометрии допускает 4 ≤ m ≤ 32, sink + window ≤ 2052 и размерность головы до 256; у Qwen3.8-27B это head_dim=256. При неподдерживаемой геометрии O(1)-граф не выполняется; для превышения runtime cap в журнале появляется o1 geometry over kernel limits, поэтому широкое окно нельзя считать запущенным на GPU без проверки журнала. Окно 2048 вместе с четырьмя sink-токенами помещается в геометрический предел, но остаётся экспериментальным; валидированный скоростной профиль статьи использует 128.
Воспроизвести измерение скорости командой bench
bench сам строит синтетический вход указанной длины. Флаг --core исключает рабочую копию, штраф повторов и per-token confidence, чтобы измерять базовый цикл в стиле llama-bench. --ignore-eos не даёт короткому EOS завершить замер, а --json печатает машиночитаемый объект.
После применения профиля выше команда O(1) имеет такой синтаксис:
cortiq bench qwen38-27b-q4tp.cmf \ --ctx 8192 --tokens 128 --ignore-eos --core \ --o1 all --o1-m 32 --o1-window 128 --o1-sink 4 --json
Контрольный запуск на том же файле:
CMF_O1_GPU=0 CMF_MTP=0 cortiq bench qwen38-27b-q4tp.cmf \ --ctx 8192 --tokens 128 --ignore-eos --core \ --o1 off --json
--ctx 8192 задаёт длину синтетического входа, а --tokens 128 — число генерируемых токенов. В отличие от готового ответа, benchmark не доказывает качество дальнего извлечения фактов: он показывает время и счётчики выбранного пути.
Что измерено на 24-ГБ карте
Итоговая серия состояла из шести чередующихся запусков: три контрольных и три с новым GPU-readout ядром. В каждом было 8192 входных и 128 выходных токенов, Vulkan, Q4TP, пакет 128, COOP1, O(1) с m=32, окном 128, четырьмя sink-токенами и префиксом 256; MTP был выключен.
Показатель | Контроль | Новое ядро | Изменение |
|---|---|---|---|
TTFT, медиана | 73,821774 с | 68,311079 с | −7,46% |
Скорость обработки входа, через TTFT | 110,970 ток/с | 119,922 ток/с | +8,07% |
Последующая генерация | 25,771462 ток/с | 26,172550 ток/с | +1,56% |
Состояние O(1) на GPU | 46 236 672 байта | 46 236 672 байта | без роста |
В новом GPU-readout ядре 32 независимых столбца сжатого состояния читаются разными потоками GPU. Порядок суммирования внутри каждого столбца и формула Aggregate сохранены, поэтому это ускорение чтения, а не новая схема аппроксимации. Все шесть запусков прошли с 16 O(1)-слоями и без пропусков графа.
Стенд: RTX PRO 4000 Blackwell, 24 467 МиБ VRAM, Xeon E5-2690 v4, 64 ГБ RAM. Это сравнение двух сборок одного проекта на одной карте, а не универсальный результат для всех GPU. Из серии не получался новый непрерывный замер пика всей VRAM; число 44,1 МиБ относится только к GPU-состоянию O(1). Эти данные также не являются парным сравнением скорости CMF с Ollama: у них были разные условия и квантизации.
Где текущий подход приближённый
Текущий O(1) — эмуляция внимания уже обученной модели. Модель не училась кодировать каждый дальний факт в 32 опорных представлениях, поэтому компактное состояние может терять детали, особенно за пределами точного окна. Оператор не эквивалентен полному softmax attention, а вывод не обязан совпадать с --o1 off токен в токен.
Есть и инженерные границы:
O(1) ограничивает размер состояния внимания, но не стоимость чтения всего входа. Префилл длинного запроса всё равно требует времени, а калибровочный префикс временно использует точное состояние на CPU.
В таблице не учтены 14,3 GB весов Q4TP, 48 GDN-состояний, scratch, драйвер и хостовые копии. Реальный бюджет карты всегда больше, чем сумма одной ячейки таблицы.
Номинальное окно Qwen в 262 144 токена не означает, что O(1) доказал качество или успешный запуск на такой длине. Для строк выше 32k приведена только геометрическая экстраполяция памяти.
m=32, окно 128 и sink 4 — принятый профиль. Большое окно 2048 экспериментально и не является рекомендацией по качеству.Обычный Ollama может включить Q8 или Q4 KV-кэш; это уменьшает линейный коэффициент примерно в два или четыре раза, но не меняет O(N) рост. Применимость зависит от модели и backend.
Диагностика
Не найден Vulkan-адаптер. Проверьте cortiq gpu, установку loader-пакетов и vulkaninfo --summary. На headless Linux задайте XDG_RUNTIME_DIR=/tmp. Если нужна CPU-проверка, запускайте отдельную команду с CMF_GPU=0; не приписывайте её скорость GPU.
Недостаточно VRAM. Убедитесь, что файл именно Q4TP и его SHA-256 совпадает. Проверьте, не занята ли выбранная карта другой вашей задачей, и выберите нужный адаптер через CMF_GPU_ADAPTER. Для долгого запроса оставьте запас под GDN и рабочие буферы; переход на CPU — честный контрольный вариант, а не доказательство ускорения.
Первый токен слишком медленный. Для длинного входа это ожидаемая цена CMF_O1_PREFILL=256: рантайм собирает точный префикс и запечатывает landmarks. Смотрите последующую скорость декода отдельно. Для диагностики используйте RUST_LOG=info и проверьте, что нет сообщения o1 geometry over kernel limits.
O(1) не включился на GPU. Оставьте CMF_O1_GPU=1 и CMF_KV=f32, передайте явный --o1 all, а затем проверьте журнал. Q8 KV-кэш полезен для обычного режима, но этот O(1)-профиль проверен с F32 KV. При --o1 off вы намеренно сравниваете полный attention.
Ответ хуже на дальнем факте. Повторите тот же prompt с --o1 off. Если нужна точность старого контекста, уменьшите область применения O(1) через --o1 deepN или список слоёв либо оставьте обычный режим. Увеличение окна меняет скорость и память рабочего пути и пока не имеет здесь отдельного quality gate.
Результат не воспроизводится. Закрепите ревизию HF, проверьте SHA-256, используйте одинаковый seed, backend, env-профиль и длину входа. Для скорости прогревайте GPU отдельно и сравнивайте медианы повторов; один холодный запуск смешивает загрузку весов и компиляцию шейдеров с самим декодом.
Куда развивается нативный вариант
Конвертация без обучения полезна как способ проверить формат, память и runtime на уже существующих моделях. Следующий естественный шаг — обучать или адаптировать модель с тем же механизмом фиксированной памяти, который будет использоваться при инференсе. Тогда модель сможет заранее учиться записывать и извлекать полезную информацию из ограниченного состояния, а исполнитель — использовать операции, рассчитанные на эту схему. Цель направления — одновременно улучшить точность сжатой дальней памяти и ускорить исполнение; размер будущего выигрыша должен быть подтверждён отдельными сопоставимыми измерениями.
Параллельно проект развивает быстрый нативный формат исполнения без роста KV-кэша и адаптацию моделей под тот же механизм. Цель направления — улучшить качество дальней памяти и скорость; для нативной 27B-модели нужны отдельные сопоставимые измерения.
В публичном реестре проекта указана патентная заявка США № 19/738,763 от 13 июля 2026 года, относящаяся к конвертации потокового внимания с постоянной памятью без обучения. Исходная запись находится в PATENTS.md.
Итоговая шпаргалка
# файл модели уже скачан и проверен export CMF_GPU=1 WGPU_BACKEND=vulkan XDG_RUNTIME_DIR=/tmp export CMF_GPU_ADAPTER=0 CMF_GPU_PROBE=0 CMF_KV=f32 export CMF_O1_GPU=1 CMF_O1_PREFILL=256 CMF_MTP=0 CMF_GRAPH_SPEC=0 cortiq run qwen38-27b-q4tp.cmf \ --o1 all --o1-m 32 --o1-window 128 --o1-sink 4 \ --temperature 0 --seed 42 --rep-penalty 1.0 \ --no-think --max-tokens 256 --prompt 'Ваш запрос'
Начните с этих параметров, сравните несколько своих запросов с --o1 off, а затем решайте, подходит ли компромисс дальнего контекста вашему сценарию. В этом и состоит практическая ценность режима: фиксированное состояние внимания становится измеряемым ресурсом, а потеря точности — явным параметром эксперимента.
Источники: модель Qwen3.8-27B на Hugging Face, исходный репозиторий CMF, релиз v0.6.6, официальная документация Ollama о KV-кэше.

