TL;DR. Я купил мини‑ПК с Ryzen AI 9 365, чтобы гонять большую локальную модель на NPU. Начал с Windows ради гибрида NPU+iGPU, который так и не заработал, прошёл квест с драйвером NPU, выяснил, что NPU видит только половину оперативки, переехал на Proxmox и в итоге запустил Qwen3.6–35B‑A3B через FastFlowLM прямо на хосте. Модель работает 24/7: prefill около 200 ток/с, decode 13–17 ток/с. По дороге выяснилось, что NPU обслуживает один запрос за раз, падает с double free, если клиент не дождался ответа, и умеет выгнать из памяти большую модель ради маленькой embedding‑модели. Ниже вся дорога по порядку, мини‑гайд и цифры.

Маршрут статьи
Маршрут статьи

Маршрут статьи: грабли расставлены в том порядке, в котором я на них наступал.

С чего началось

Домашний сервер был на Ryzen 7 5800U: без видеокарты и без NPU. Локальная LLM на CPU работала, но на длинных промптах всё время уходило не на ответ, а на чтение:

Фаза

Токены

Время

Скорость

Prefill (чтение промпта)

7943

245,1 с

32,4 ток/с

Decode (генерация)

36

5,0 с

7,05 ток/с

Итого

7979

250 с

-

99% времени модель читала. И тут маркетинг AMD говорит ровно то, что хочется услышать: NPU хорош именно в prefill. Хочу. Зачем мне вообще локальная модель, а не облако, расскажу в третьем акте, там же посчитаем деньги.

Выбирал между мини‑ПК на Ryzen AI 9 365 и Ryzen AI 9 HX PRO 370. Взял Ryzen AI 9 365 (NPU XDNA2, Radeon 880M) и 64 ГБ RAM. Тогда 64 ГБ казались запасом. Позже выяснилось, что это минимум.

Мини-ПК FIREBAT на Ryzen AI 9 365
Мини‑ПК FIREBAT на Ryzen AI 9 365

Конкретно — мини‑ПК FIREBAT на Ryzen AI 9 365. Сама коробка без памяти и SSD стоила 30 тыс. ₽, SSD переехал со старого мини‑ПК, а 64 ГБ DDR5-4800 (2 × 32 ГБ) обошлись ещё в 40 тыс. ₽. Итого около 70 тыс. ₽ (~$825), и память оказалась дороже самого компьютера.

Чем вообще запускать LLM на NPU

Первое открытие: привычный стек тут не помогает.

  • llama.cpp / Ollama / LM Studio про NPU не знают. CPU, GPU через Vulkan или ROCm, а NPU простаивает.

  • Lemonade Server от AMD обещает самое вкусное: гибрид, где NPU считает prefill, а встроенная графика — decode. Но гибрид работает только с ONNX‑моделями, которые AMD заранее подготовила, и официально только на Windows. И выбор там скромный: в актуальном Ryzen AI 1.8 это Llama 3.2 1B/3B, Llama 3.1 8B, Qwen3 1.7B/4B/8B, Qwen2.5 до 7B, Mistral 7B, Phi-4-mini. Потолок — 8B, в прошлой версии попадались Qwen2.5–14B и Qwen3-14B.

  • FastFlowLM (flm) — отдельный рантайм под NPU AMD. Отдаёт OpenAI‑совместимый API, модели берёт из своего каталога на Hugging Face в формате .q4nx. Есть сборки под Windows и Linux. И в каталоге есть Qwen3.6–35B‑A3B с разбором картинок.

Мне хотелось модель побольше: чем больше, тем лучше, и чтобы умела разбирать картинки. Хотелось Qwen3.6–35B. Но гибрид всё равно был слишком заманчивым, чтобы его не проверить. Картинка в голове была такая: NPU быстро читает промпт, встроенная графика генерирует ответ, каждый чип занят своим делом. Гибрид живёт на Windows, значит, начинаем с Windows.

Акт 1. Windows

Lemonade: посмотрел и закрыл

Первым делом поставил Lemonade и открыл список моделей. Всё, что помечено как Hybrid, — те самые мелкие модели, которые даже тестировать не хочется. Для Qwen3.6–35B гибрида нет: либо NPU через FLM, либо iGPU через llama.cpp, но не оба сразу. На этом мечта о гибриде закончилась, и я перешёл на FLM без всякого Lemonade.

Квест: драйвер NPU

Ставлю FLM, ввожу flm, flm help, flm validate — тишина. Ни вывода, ни ошибки. Код возврата -1073741511, то есть 0xC0000139: бинарник не нашёл нужную функцию в DLL. Виноват старый драйвер NPU.

Найти свежий драйвер NPU — отдельный квест. Последние драйверы с сайта AMD не означают последний драйвер NPU. Отдельно он лежит в составе Ryzen AI Software, но чтобы его скачать, нужно зарегистрироваться и пройти проверку, что ты не из «недружественной» для AMD страны. Из России, Казахстана и Нидерландов доступ я так и не получил.

Изрядно попотев, драйвер я всё‑таки обновил, и flm ожил.

Модель есть, памяти нет

Qwen3.6–35B‑A3B скачалась, запустилась, отвечает. Подключаю Open WebUI, задаю вопрос, и flm падает с DMA Paging Error 0xc01e0200. Не всегда, а когда UI шлёт два запроса подряд: скрытую генерацию заголовка чата и сам ответ.

Открываю «Диспетчер задач»:

  • Windows видит 47,6 ГБ из 64.

  • «Выделенная память GPU» — 15,8 ГБ, занят из них 1 ГБ. BIOS заранее откусил их под встроенную графику, и они простаивают.

  • «Общая память» у NPU — 23,8 ГБ. Ровно половина от 47,6.

То есть NPU доступно около 50% оперативки, которую видит ОС, а резерв BIOS под iGPU срезает и саму эту базу. Модель весит ~20 ГБ, на KV‑cache и второй запрос остаётся 3,8 ГБ. Отсюда и падения.

Лечится в BIOS: UMA Frame Buffer Size (он же iGPU Memory) в минимум.

Пул NPU до и после правки BIOS
Пул NPU до и после правки BIOS

После правки: ОС видит 63,1 ГБ, пул NPU 31,6 ГБ, модель занимает 21,2 из них.

Вывод для тех, кто выбирает железо: на 32 ГБ такая модель не живёт. В issue #242 у людей с 32 ГБ даже GPT‑OSS-20B (~14 ГБ) не влезала стабильно. Считайте так: модель плюс запас на контекст должны помещаться в половину RAM за вычетом резерва под iGPU.

«NPU загружен всего на 50%»

Модель работает, смотрю на график NPU: пила на 50–60%. Первая мысль — что‑то недонастроено. Нет. У MoE‑модели с ~3B активных параметров на токен вычислений мало, и NPU ждёт память: по документации FLM полоса у NPU около 60 ГБ/с против ~125 ГБ/с у iGPU. Decode упирается в память, так что 100% не будет.

И Open WebUI туда же

Попутная находка: Open WebUI с включёнными инструментами уходит в бесконечный цикл. Модель с 3B активных параметров генерирует дубли tool_calls, UI ругается и отправляет весь диалог заново, с полным re‑prefill. Лимит CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS по умолчанию 256; ставьте 2–3.

Акт 2. Почему всё‑таки Linux

И тут в голову пришла простая мысль: а зачем я страдаю на Windows? Гибрид NPU+iGPU, ради которого я сюда пришёл, уже провалился, работать будет только NPU, а FLM одинаково ставится и на Linux. К тому же на этой машине я хотел поднять свои виртуалки, а возиться с виртуализацией на Windows не хотелось. Итог: Proxmox VE 9 (Debian 13).

План А: LLM в виртуалке

Раз всё остальное живёт в ВМ, логично и LLM туда же. Классика: ВМ с Ubuntu, пробрасываем устройство, внутри flm. Не работает. Встроенная графика через VFIO пробрасывается, NPU — нет: софт к этому пока не готов. На форуме Proxmox даже тема так и называется: «iGPU funktioniert — NPU aktuell nicht».

План Б: FLM прямо на хосте

Драйвер NPU живёт в ядре хоста, поэтому flm ставится на сам Proxmox, рядом с гипервизором. Арифметика по памяти: 64 ГБ, из них ~21 ГБ модель, 10–15 ГБ виртуалки. Виртуалкам — жёстко зарезервированную память без ballooning, иначе в момент инференса хост может не найти страниц под буферы flm.

Приятный сюрприз после Windows: на Linux драйвер NPU уже есть в ядре Proxmox, никаких регистраций.

Мини‑гайд: FLM на Proxmox 9 / Debian 13

Компонент

Версия

CPU

AMD Ryzen AI 9 365 w/ Radeon 880M (NPU XDNA2)

RAM

64 ГБ, UMA Frame Buffer в BIOS — минимум

ОС

Proxmox VE 9.2 (Debian 13 trixie)

Ядро

7.0.2-6-pve, драйвер amdxdna в ядре, прошивка NPU 1.1.2.64

XRT

libxrt2 / libxrt-npu2 2.25.0–4 (deb из Debian sid)

FastFlowLM

1.0.5

Модель

qwen3.6-moe:35b-a3b: ~20 ГБ, MoE, ~3B активных параметров, vision

1. Проверяем, что ядро видит NPU

uname -r                 # свежее ядро, у меня 7.0.2-6-pve
lsmod | grep amdxdna     # драйвер загружен
ls -l /dev/accel/        # должен быть accel0
dmesg | grep -i xdna     # прошивка загрузилась без ругани

На Proxmox 9 всё это было «из коробки». На старых ядрах бывает Incompatible firmware protocol — тогда обновлять ядро.

2. XRT и FastFlowLM

FLM зависит от XRT (userspace‑часть для NPU). В стабильном trixie этих пакетов нет, поэтому я не подключал sid целиком, а взял два готовых deb‑файла из его пула:

cd /tmp
wget https://github.com/ROCm/FastFlowLM/releases/download/v1.0.5/fastflowlm\_1.0.5\_debian13\_amd64.deb
wget http://deb.debian.org/debian/pool/main/x/xrt/libxrt2\_2.25.0-4\_amd64.deb
wget http://deb.debian.org/debian/pool/main/x/xrt/libxrt-npu2\_2.25.0-4\_amd64.deb

apt install -y ./libxrt2_2.25.0-4_amd64.deb ./libxrt-npu2_2.25.0-4_amd64.deb \
               ./fastflowlm_1.0.5_debian13_amd64.deb

flm validate      # ядро, устройство, прошивка, memlock - всё зелёное

Версии в пуле sid со временем меняются: если ссылка отдаёт 404, актуальное имя файла ищите на packages.debian.org/sid/libxrt2.

memlock. FLM нужен unlimited memlock, иначе не выделятся буферы под NPU. Deb‑пакет прописал его сам. Если ulimit -l в SSH‑сессии показывает другое, это особенность non‑interactive shell.

3. Модель

flm list                        # ✅ скачано, ⏬ нет
flm pull qwen3.6-moe:35b-a3b    # ~20 ГБ, запускать в tmux

4. Сервер под systemd

# /etc/systemd/system/flm-serve.service
[Unit]
Description=FastFlowLM serve (qwen3.6-moe:35b-a3b)
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/flm serve qwen3.6-moe:35b-a3b --host 0.0.0.0 --port 52625 --ctx-len 131072
Restart=on-failure
RestartSec=5
User=root

[Install]
WantedBy=multi-user.target

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

[FLM]  WebServer started on port 52625 with 10 I/O threads

Restart=on-failure и --ctx-len тут не для красоты. Зачем они, станет ясно в третьем акте.

5. Проверка

root@proxmox:~# curl http://localhost:52625/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen3.6-moe:35b-a3b","messages":[{"role":"user","content":"Привет! /no_think"}]}'
{
  "model": "qwen3.6-moe:35b-a3b",
  "choices": [{ "message": { "role": "assistant", "content": "Привет! Чем могу помочь?" },
                "finish_reason": "stop" }],
  "usage": {
    "prompt_tokens": 16,
    "completion_tokens": 9,
    "kv_token_occupancy_rate_percentage": 0.019,
    "prefill_duration_ttft": 2.7097,
    "decoding_duration": 0.6241,
    "prefill_speed_tps": 5.90,
    "decoding_speed_tps": 14.42
  }
}

Помимо стандартного usage, FLM отдаёт честные метрики движка: время и скорость prefill и decode. Сохраняйте их, они ещё пригодятся.

И сразу первый повод присмотреться: prefill 5,9 ток/с на 16 токенах. Это не поломка. У Qwen3.6–35B‑A3B на NPU есть фиксированная задержка около 2,7 с на любой запрос: prefill раскладывается на ~4900 последовательных вызовов NPU по экспертам (issue #710). На коротком промпте эти 2,7 с и есть всё время, на длинном они теряются.

Акт 3. Ради чего всё это

Теперь о том, зачем мне понадобилась локальная модель. Мои сервисы шлют частые запросы до ~32k токенов, в сумме около 6 млн токенов в сутки: 5,5 млн входящих и 0,5 млн исходящих. Сколько это стоит в облаке по ценам на сентябрь 2026:

Модель

Вход / выход, $ за 1M

В сутки

В месяц

В год

DeepSeek Flash

0,15–0,30 / 0,60–1,20

$1,1–2,3

$34–68

$411–821

DeepSeek V4 Pro

0,66–1,32 / 1,98–3,96

$4,6–9,2

$139–277

$1 686–3 373

OpenAI GPT-5.6 Luna

0,20 / 1,20

$1,7

$51

$621

OpenAI GPT-6 Sol

2 / 10

$16

$480

$5 840

Claude Haiku 4.5

1 / 5

$8

$240

$2 920

Claude Sonnet 5

2 / 10

$16

$480

$5 840

Claude Opus 5.5

4 / 20

$32

$960

$11 680

Без учёта кеша промптов и batch‑скидок. У DeepSeek вилка — это ночной и дневной тариф.

Для сравнения: весь мини‑ПК обошёлся в ~$825. Против Claude Sonnet 5 или GPT-6 Sol он окупается меньше чем за 2 месяца, против Claude Haiku 4.5 — примерно за 3,5 месяца, против самого дешёвого DeepSeek Flash — за год‑два.

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

Пока с моделью разговариваешь в чате, всё прекрасно. Но как только к ней подключились сервисы, которые шлют запросы сами, пачками и круглосуточно, началось самое интересное.

Сюрприз 1: время ответа скачет в разы

Запросы одного размера выполнялись то за 35 секунд, то за 170. Метрики FLM всё объяснили: NPU обслуживает строго один запрос за раз. У flm serve есть FIFO‑очередь (--q-len, по умолчанию 10), но параллелизма нет.

Таймлайн двух запросов в очереди NPU
Таймлайн двух запросов в очереди NPU

Два запроса пришли в одну секунду. Расчётное время Б — 55,6 с, фактическое — 114,7 с. Разница равна времени запроса А.

Сюрприз 2: double free

Очередь — полбеды. У HTTP‑клиентов стоял дефолтный таймаут 120 секунд. Запрос ждёт в очереди, клиент не дожидается и отменяет его, а FLM на отмене во время prefill падает целиком. Вместе с ним пропадает и вся очередь:

Client disconnected; cancelling active request
Prefill Cancelled
Clearing context...
double free or corruption (!prev)      # или просто SIGSEGV

Худший случай: один сервис накидал 26 параллельных запросов с max_tokens: 70000. Очередь росла от 1 до 26 и ни разу не убывала, потом пошла лавина отмен, и flm лёг. Этот баг открыт в апстриме: issue #608.

Что помогло:

  1. Таймауты клиентов — минуты, у меня 20 на тяжёлые задачи. Пусть лучше ждёт, чем отменяет.

  2. Restart=on-failure: systemd поднимает flm за ~15 с. Баг остался, но перестал будить меня ночью.

  3. --ctx-len явно. По умолчанию 32k, и промпт на ~38k токенов давал max length reached during prefill и тот же крэш.

  4. Тяжёлые промпты — не на NPU. Запрос на ~38k токенов держал NPU 6–7 минут, всё остальное копилось сзади. Такие задачи я увёл во внешнее API.

Сюрприз 3: Connection limit reached (10)

Когда к NPU ходили уже три независимых сервиса, каждый со своей конкурентностью, в логе FLM появилось Connection limit reached (10), rejecting new connection. Каждый сервис по отдельности вёл себя прилично, но о других ничего не знал.

Первая идея — --q-len 1, чтобы FLM сам жёстко сериализовал запросы. Стало хуже: один зависший запрос занимает единственный слот, остальные получают 503. С очередью 10 зависание — это задержка, с очередью 1 — полный отказ. Вернул дефолт.

Рабочее решение — один вышибала на входе: LiteLLM‑прокси (llm‑proxy) перед flm с глобальным лимитом на весь трафик.

Фейс-контроль llm-proxy перед NPU
Фейс‑контроль llm‑proxy перед NPU
model_list:
  - model_name: flm-chat
    litellm_params:
      model: openai/qwen3.6-moe:35b-a3b
      api_base: http://<flm-host>:52625/v1
      api_key: "not-needed"

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
  global_max_parallel_requests: 3      # потолок на весь трафик к NPU
  store_prompts_in_spend_logs: true    # иначе в Request Logs "Data Not Available"
  • Разные model_name на один бэкенд — дешёвый способ видеть в логах, какой клиент сколько ест, без virtual keys и базы.

  • Пробы прокси вешайте на /health/liveliness. Обычный /health дёргает сам flm, а тот отвечает минутами.

  • Колонке TTFT в логах LiteLLM не верьте: для нестриминговых запросов она равна общему времени. Настоящий TTFT лежит в usage от FLM, в поле prefill_duration_ttft.

Сюрприз 4: thinking не выключается

Сервисам нужен короткий JSON, а не рассуждения. Ни enable_thinking: false, ни chat_template_kwargs, ни think на Qwen3.6 через flm не действуют. Это болезнь самой Qwen3.6, в vLLM, SGLang и llama.cpp то же самое. Работает /no_think прямо в промпте: 59 токенов ответа без него и 2 с ним. Для задач «ответь строго JSON» хватает и явной инструкции.

Сюрприз 5: embedding выгоняет большую модель

Для поиска похожих текстов понадобились эмбеддинги. В каталоге FLM есть embed-gemma:300m, включается флагом --embed 1. Пока NPU свободен, всё отлично: 0,29 с на вектор из 768 чисел. Под нагрузкой рядом с 35B‑моделью запрос висит минутами, а иногда в логе появляется:

[ERROR] Unsupported model family or non-llm: "embed-gemma"

После этого flm выгружает 35B, грузит llama3.2:1b, отвечает ею и грузит 35B обратно. Несколько минут простоя для всех клиентов.

Решение: та же embeddinggemma-300M в GGUF Q8_0 через llama.cpp на CPU, отдельной записью в том же llm‑proxy. Результат: 0,06–0,11 с на запрос, 15 запросов подряд без единого сбоя. Маленькой модели NPU не нужен, а общую очередь она ломает.

Цифры

Все замеры ниже — Qwen3.6–35B‑A3B на Ryzen AI 9 365 через FLM 1.0.5, реальные запросы из логов, без очереди.

Запрос

Вход, ток.

Выход, ток.

Prefill, ток/с

Decode, ток/с

«Привет» из curl выше

16

9

5,9*

14,4

Текст

1 046

192

109,1

17,1

Картинка + промпт

1 127

61

99,1

16,9

Текст

5 699

98

193,2

15,6

Текст

6 135

103

196,9

15,7

Текст

6 902

405

205,6

15,7

Текст

17 653

1 187

209,9

13,3

Текст

17 980

1 375

211,4

13,2

* фиксированные ~2,7 с на запрос, см. «Ссылки».

Скорость prefill и decode от размера промпта
Скорость prefill и decode от размера промпта

Картина понятная: чем длиннее промпт, тем быстрее prefill (фиксированные накладные расходы размазываются) и тем медленнее decode (растёт KV‑cache). Картинка лишь немного медленнее текста того же размера. Для прикидки на промптах 5–18k токенов: prefill ~200 ток/с, decode ~13–16 ток/с.

Официальный бенчмарк FLM для той же модели (Ryzen AI 7 350, 96 ГБ)

Контекст

Prefill, ток/с

Decode, ток/с

1k

102

17,5

32k

281

11,2

Мои цифры с ним хорошо сходятся: на 1k токенов почти один в один.

CPU, NPU и RTX 3090

CPU, NPU и RTX 3090: prefill и decode
CPU, NPU и RTX 3090: prefill и decode

Против старого CPU выигрыш заметный: prefill в ~6 раз, decode в ~2 раза. Среднее время вызова упало с ~513 с до ~78 с, но это смесь «NPU быстрее» и «очередь на CPU была хуже».

Против видеокарты NPU проигрывает всухую: у RTX 3090 на той же модели в Q4 prefill ~2400–2600 ток/с (в 13 раз быстрее), decode ~60–75 ток/с (в 4–5 раз). Выигрывает NPU в другом: тишина, энергопотребление и 35B‑модель в коробке размером с коробку из-под печенья.

Итого: стоит ли оно того

Да, если:

  • нужна большая MoE‑модель 24/7 в тихой коробке

  • нагрузка — фоновая очередь задач, а не живой чат

  • промпты длинные, ответы короткие

Нет, если:

  • нужен параллелизм: NPU один, очередь одна

  • нужна интерактивность: 13–17 ток/с в чате медленно

  • модель с контекстом больше половины RAM

А у вас есть опыт с NPU от AMD или Intel? Кто‑нибудь завёл гибрид NPU+iGPU на Linux на модели крупнее 8B? Делитесь в комментариях: информации по этой теме мало, и каждая история на вес золота.

Ссылки