Локальный LLM-сервер на AMD Strix Halo: запуск Qwen3.8-Flash-Next через HALOGEN в Fedora 43

Ещё недавно «запустить LLM у себя» почти всегда означало одно: discrete GPU с парой десятков гигабайт VRAM. AMD Strix Halo с 128 ГБ унифицированной памяти эту привычку ломает — крупные MoE-модели на нём заводятся спокойно, если inference-движок умеет работать с общей памятью, memory mapping и своеобразием встроенной графики.
Ниже — практическая настройка Qwen3.8-Flash-Next на Fedora 43 Server через HALOGEN: OpenAI-совместимый API, ключ, четыре параллельные сессии и доступ снаружи через порт 9091.
Что за зверь Qwen3.8-Flash-Next
Это не «обычная» модель даже по меркам MoE. Внутри — смесь экспертов (MoE), гибридное внимание и отдельные N-gram embeddings. Если коротко:
Параметр | Значение |
|---|---|
Основная модель | 125 млрд параметров |
Активируется на токен | около 6 млрд параметров |
N-gram embedding table | около 51 млрд параметров |
MTP-head | около 4 млрд параметров |
Количество слоёв | 48 |
MoE-экспертов | 512 routed + 1 shared |
Активных routed-экспертов | 10 |
Нативный контекст | 262 144 токена |
Максимальный расширенный контекст | до 1 000 000 токенов |
Возможности | текст и опционально изображения |
В чём фокус MoE: суммарная ёмкость модели огромная, но на каждом токене она считает лишь малую часть весов. Поэтому и получается скорость выше, чем у плотной модели такого же полного размера.
Почему HALOGEN, а не llama.cpp
У Strix Halo unified memory architecture: CPU и встроенный GPU делят одну LPDDR5X-память. Отдельной физической VRAM, как у дискретных NVIDIA или Radeon, здесь просто нет.
HALOGEN рассчитан именно на такое устройство:
заточен под GPU gfx1151;
хранит веса в собственном формате
.hgn;не пытается копировать весь комплект весов в «VRAM»;
держит N-gram lookup table через memory mapping;
ускоряет генерацию с помощью MTP/speculative decoding;
отдаёт наружу OpenAI-совместимый HTTP API.
Это не универсальная замена llama.cpp или vLLM — все его преимущества вытекают из узкой заточки под одну архитектуру GPU и одну линейку моделей.
И сразу важный момент про BIOS: параметр UMA Frame Buffer выставляйте в минимальное фиксированное значение, а не оставляйте Auto. HALOGEN нужен доступ к обычной памяти хоста, а большой статический резерв под iGPU только съедает доступную RAM.
Сколько места нужно на диске
Стандартный набор весов HALOGEN выглядит так:
Файл | Размер | Назначение |
|---|---|---|
qwen38-flash-next-v2.hgn | 62,1 GiB | Основной checkpoint |
qwen38-flash-next-ngram.hgn | 47,7 GiB | N-gram lookup table |
tokenizer/ | небольшой | Токенизатор |
Всего | около 110 GiB | Текстовая модель |
qwen38-flash-next-vision.hgn | 0,84 GiB | Опциональная поддержка изображений |
qwen38-flash-next-mtp.hgn | 1,42 GiB | Draft-head для стороннего GGUF |
При первом запуске HALOGEN сам скачает нужные файлы, если передать ему:
-e HALOGEN_DOWNLOAD=peonist-ai/halogen-qwen3.8-flash-next
Свободного места закладывайте не меньше 120–140 ГБ: помимо самих весов понадобятся служебные файлы и запас под обновления.
Какую скорость ждать
По опубликованным результатам HALOGEN на Strix Halo ориентиры такие:
Сценарий | Производительность |
|---|---|
Генерация текста | около 54 токенов/с |
Благоприятный сценарий speculative decoding | до 94 токенов/с |
Генерация при редактировании кода | до 81 токена/с |
Генерация tool calls | около 71 токена/с |
Prefill: промпт 8K токенов | около 1 821 токена/с |
Prefill: промпт 32K токенов | около 1 740 токенов/с |
Prefill: промпт 131K токенов | около 1 675 токенов/с |
Что это значит на практике? Новый документ на 32K токенов обработается примерно за 19 секунд. Уже закэшированная история диалога продолжится заметно быстрее. Ответ на 1 000 токенов при 54 токенах/с будет крутиться секунд 18–19. А реальная скорость, конечно, зависит от текста, длины контекста, температуры, speculative decoding и того, что ещё грузит машину параллельно.
При четырёх одновременных сессиях HALOGEN просто делит вычислительный ресурс между потоками. Ориентир из документации: примерно 76 токенов/с суммарно и 11–19 токенов/с на поток. Так что четыре слота полезны для API-сервера с несколькими клиентами, но единичный запрос они не ускоряют.
Синтетические тесты: проверяем на живом сервере
Цифры из readme — это хорошо, но интереснее погонять бенчмарк по реальному эндпоинту. Подошёл LLMPerf от ray-project: он собирает промпты из случайных кусков Шекспира и меряет по каждому запросу время до первого токена (TTFT) и интервалы между токенами (ITL) — из них легко получить скорость чтения и записи.
По ходу дела выяснились две грабли, о которых стоит знать заранее:
SSE-парсер llmperf пришлось патчить. На длинных контекстах при c=4 стрим молчит достаточно долго, и прокси начинает слать keep-alive (
:ping), от которых наивный парсер падает. Лечится просто: игнорировать комментарии, разбирать только строкиdata:.Модель — reasoning-модель. Она «размышляет» перед ответом (поле
reasoning_content), и эти паузы искажают метрики: на 128k свободная генерация показывала жалкие 2.6 ток/с записи. Отключается штатным для Qwen3 параметромchat_template_kwargs: {"enable_thinking": false}, а чтобы модель не останавливалась по EOS после пары сотен токенов, добавлялиignore_eos: true.
Финальная серия — reasoning OFF, один поток, выход = 1/10 контекста, 12 запросов без единой ошибки:
Контекст | Выход (факт) | TTFT p50 | Чтение | Запись | E2E p50 |
|---|---|---|---|---|---|
8k | 888 | 4.2 с | 1 965 ток/с | 96.4 ток/с | 13.3 с |
16k | 1 775 | 8.1 с | 2 040 ток/с | 98.1 ток/с | 26.6 с |
32k | 3 571 | 16.1 с | 2 048 ток/с | 96.5 ток/с | 53.4 с |
64k | 7 203 | 32.3 с | 2 035 ток/с | 96.5 ток/с | 121 с |
128k | 14 518 | 66.0 с | 1 992 ток/с | 94.5 ток/с | 230 с |
Что из этого следует:
Запись — константа ~95–98 ток/с на всём диапазоне контекстов. «Деградации от длины контекста» не существует — её имитировали паузы размышлений (разница до 36×). И обещанные до 94 токенов/с в благоприятном сценарии — не маркетинг: форсированный стрим стабильно выдаёт ~96.
Чтение — линейное, ~2 000 ток/с. TTFT растёт с контекстом (4.2 с на 8k → 66 с на 128k), и именно чтение здесь узкое место.
Параллелизм ведёт себя как заявлено. Короткие контексты при c=4 дают максимум суммарного throughput (+27% к одиночному потоку), но батчинг замедляет каждый запрос по TTFT в 2–5 раз, а c=16 сервер уже не переваривает. Длинные одиночные запросы выгоднее слать по одному.
Для агентского сценария (прочитать контекст + ответ ~150 токенов) шаг целиком занимает ~6 с при 8k, ~18 с при 32k и ~68 с при 128k — комфортный рабочий контекст где-то до 32k.
А если оставить reasoning включённым?
Для полноты картины ту же матрицу прогнали и с включёнными размышлениями. Картинка получается занятная:
Метрика | Reasoning ON | Reasoning OFF |
|---|---|---|
Запись @ 8k, форсированный вывод | ~90–99 ток/с | 96.4 ток/с |
Запись @ 8k, свободный вывод | 26.6 ток/с | — |
Запись @ 128k, свободный вывод | 2.6 ток/с | 94.5 ток/с |
TTFT до контента на тривиальном вопросе | 6.76 с | 0.18–0.35 с |
Тонкость в том, что метрики здесь искажает сам способ измерения. TTFT отсчитывается до первого контентного токена, то есть включает все размышления целиком. А между контентными токенами вклиниваются паузы «подумать» — на 128k интервал доходит до 378 мс, что и выглядело как деградация до 2.6 ток/с. При форсированном выводе, когда паузы вымываются сплошным стримом, скорость записи почти не отличается от reasoning OFF (~90–99 против 96.4 ток/с): токены генерируются так же быстро — модель просто думает между ними.
Практический вывод: нужен быстрый и предсказуемый стрим — отключайте thinking. Если качество важнее latency — оставляйте, но помните, что свободная генерация с reasoning останавливается по EOS уже на 85–365 токенах, так что «честную» скорость записи вы в этом режиме всё равно не увидите.
И приятный бонус: за всё исследование — 150+ запросов, и после патча парсера ни одной ошибки и ни одного таймаута.
Готовим Fedora 43 Server
Ставим Podman и базовые утилиты:
sudo dnf upgrade --refresh -y
sudo dnf install -y podman curl pciutils
sudo reboot
После перезагрузки проверяем драйвер и GPU-устройства:
lspci -nnk | grep -A 4 -Ei 'VGA|Display|3D'
ls -l /dev/kfd /dev/dri/renderD*
free -h
Для работы с GPU пользователю обычно нужны группы render и video:
sudo usermod -aG render,video "$USER"
Дальше — важная мелочь, о которой часто забывают: полностью завершите SSH-сеанс и подключитесь заново, иначе новые группы не подхватятся.
Создаём API-ключ
HALOGEN умеет защищать API через переменную HALOGEN_API_KEY. Клиент передаёт её стандартным заголовком:
Authorization: Bearer ВАШ_КЛЮЧ
Создадим файл окружения с правами доступа только для текущего пользователя:
mkdir -p "$HOME/.config/halogen" "$HOME/halogen-models"
chmod 700 "$HOME/.config/halogen"
(
umask 077
if [ ! -f "$HOME/.config/halogen/server.env" ]; then
printf 'HALOGEN_API_KEY=%s\n' \
"$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" \
> "$HOME/.config/halogen/server.env"
fi
)
chmod 600 "$HOME/.config/halogen/server.env"
Посмотреть ключ можно так:
cat "$HOME/.config/halogen/server.env"
И банальное, но необходимое: не добавляйте этот файл в Git, не пересылайте в мессенджерах и не держите в публичных конфигурациях.
Запускаем HALOGEN с четырьмя параллельными сессиями
HALOGEN слушает порт 8731 внутри контейнера. Если внешний API должен быть доступен через 9091, порт-маппинг выглядит так: 9091 на хосте → 8731 в контейнере.
Команда запуска:
podman run -d \
--name halogen \
-p 0.0.0.0:9091:8731 \
--device /dev/kfd \
--device /dev/dri \
--group-add keep-groups \
--security-opt label=disable \
--ipc=host \
--ulimit memlock=-1:-1 \
--env-file "$HOME/.config/halogen/server.env" \
-e HALOGEN_KV_SLOTS=4 \
-e HALOGEN_DOWNLOAD=peonist-ai/halogen-qwen3.8-flash-next \
-v "$HOME/halogen-models:/models" \
ghcr.io/peonist-ai/halogen-flash-server:0.18.5
Что здесь отвечает за что
Параметр | Назначение |
|---|---|
| Публикует API наружу на порту 9091 |
| Доступ к AMD Compute Runtime |
| Доступ к DRM/GPU |
| Передаёт контейнеру права групп пользователя |
| Обход ограничений SELinux-разметки для GPU-доступа |
| Общий IPC namespace, как рекомендует quickstart-конфигурация |
| Снимает лимит блокируемой памяти |
| До четырёх одновременно генерирующих диалогов |
| Автоматическая загрузка весов при первом старте |
| Подключает API-ключ, не светя им в командной строке |
Посмотреть ход загрузки и инициализации модели:
podman logs -f halogen
И завершающий штрих — на мой взгляд, обязательная настройка. Разрешите пользователю работать в фоне, иначе при закрытии SSH-консоли сервер просто выключится:
sudo loginctl enable-linger "$USER"
Почему nvtop показывает всего ~27 ГБ «видеопамяти»
На Strix Halo показания nvtop регулярно вызывают вопросы. Модель занимает около 110 ГиБ на SSD, основной checkpoint требует примерно 62 ГиБ резидентной памяти — а nvtop показывает какие-то ~27 ГБ GPU memory.
Это нормально, и вот почему.
Отдельной физической VRAM у Strix Halo нет: iGPU использует RAM хоста, и HALOGEN не обязан отображать весь объём модели как классическую GPU VRAM. Память распределяется примерно так:
основные веса живут в резидентной памяти и занимают около 62 ГиБ;
N-gram table на 47,7 ГиБ подключена через mmap — Linux подтягивает её страницы по мере необходимости и греет их в page cache.
В nvtop при этом обычно хорошо видны буферы GPU, рабочие области вычислений, KV-cache и часть GPU memory mapping. При HALOGEN_KV_SLOTS=4 заметную долю тех самых ~27 ГБ съедает общий KV-pool и состояние слотов.
Так что цифра в nvtop не означает, что модель загрузилась «только на 27 ГБ» или работает не в полную силу.
Расширяем контекст до миллиона токенов через YaRN
Нативный контекст модели — 262 144 токена, но это не потолок. HALOGEN умеет растягивать позиционный контекст через YaRN, и задаётся это двумя переменными окружения:
-e HALOGEN_ROPE_YARN=4 \
-e HALOGEN_CTX=1048576 \
В такой конфигурации максимальная длина контекста вырастает с 262 144 до 1 048 576 токенов. Важно: настройки читаются при запуске, поэтому контейнер нужно пересоздавать, а не просто перезапускать.
Сколько это съедает на практике? Живой замер при миллионном контексте и одном слоте: HALOGEN занимает 36,7 ГиБ по данным nvtop и 38,9 ГиБ по htop (для сравнения — около 27 ГиБ в nvtop на нативном контексте с четырьмя слотами). То есть сам по себе миллион запускается вполне уютно. Но «запускается» и «работает как станция» — вещи разные: если машина нужна именно как полноценная станция с миллионным контекстом, закладывайте порядка 96 ГиБ памяти — под веса, выросший KV-пул и запас под page cache. 128-гигабайтный Strix Halo такое закрывает с запасом, а на 64 ГБ замахиваться на миллион уже не стоит.
Для первого захода с таким контекстом советую один слот (HALOGEN_KV_SLOTS=1) — незачем делить и без того растянутый KV-пул между четырьмя запросами. Веса из $HOME/halogen-models и API-ключ из env-файла никуда не денутся, ничего заново качать не придётся. А чтобы не трогать исходный контейнер, второй заведём под другим именем — halogen-yarn:
podman run -d \
--name halogen-yarn \
-p 0.0.0.0:9091:8731 \
--device /dev/kfd \
--device /dev/dri \
--group-add keep-groups \
--security-opt label=disable \
--ipc=host \
--ulimit memlock=-1:-1 \
--env-file "$HOME/.config/halogen/server.env" \
-e HALOGEN_KV_SLOTS=1 \
-e HALOGEN_ROPE_YARN=4 \
-e HALOGEN_CTX=1048576 \
-e HALOGEN_DOWNLOAD=peonist-ai/halogen-qwen3.8-flash-next \
-v "$HOME/halogen-models:/models" \
ghcr.io/peonist-ai/halogen-flash-server:0.18.5
Единственный нюанс: если основной контейнер halogen всё ещё работает, порт 9091 у него занят — остановите его перед запуском второго, два контейнера не смогут слушать один и тот же порт.
Не публикуйте HTTP API в интернете без TLS
API-ключ защищает сервер от чужих запросов, но шифровать соединение он не умеет. По чистому HTTP токен и содержимое запросов может перехватить любой, кто стоит по пути.
Правильная production-схема выглядит так:
Интернет
↓ HTTPS :9091
Caddy / Nginx / Traefik
↓ HTTP 127.0.0.1:8731
HALOGEN
Вместо итога
Связка из AMD Strix Halo, 128 ГБ RAM, Fedora 43 Server и HALOGEN позволяет поднять быстрый локальный API для Qwen3.8-Flash-Next — без дискретной GPU. На выходе получается:
MoE-модель на 125B параметров с 6B активных на токен;
около 110 ГиБ весов на диске;
50+ токенов/с для одного интерактивного потока на рабочих задачах при отключенном reasoning;
до четырёх параллельных генераций через
HALOGEN_KV_SLOTS=4;OpenAI-совместимые
/v1/chat/completions,/v1/completionsи/v1/responses;доступ извне через порт 9091;
авторизация через
HALOGEN_API_KEY.
Главное отличие этой конфигурации от «просто LLM-сервера» — правильная работа с UMA-памятью: низкий UMA carve-out в BIOS, понимание роли memory-mapped N-gram table и отказ от идеи читать показания nvtop как полный объём памяти, занятой моделью.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.