#ai #qwen #local #test #prj #way
Прогресс есть:
с 13 до 20 ток/с там, где было нормально
с 5,4 до 7 ток/с там, где было совсем тяжело
Qwen3.8-Flash-Next 125B A6B + 51B N-Gram MoE (Thinking)
Пост не про бенчмарки. Он про то, сколько можно выжать из того, что уже стоит на столе. Железо то же, модель та же, контекст не урезан.
---
Было и стало
Сравниваю не одну сессию с другой, а две свои сборки: прошлую и нынешнюю. Цифры -- сводные по множеству рабочих сессий, каждая из них разная, поэтому я свёл к характерным значениям. Смотрел по своим прогонам, в лабораторию не ходил:
На старте сессии: было 13, стало 20+
Половина окна (256k): было 8,2, стало 10+
Заполненное под 240k: было 5,4, стало ~7
Ускорение держится по всей глубине окна, а не только на лёгком старте. И именно этот хвост мне важнее всего: это не конец сессии, а штатный режим работы. Ниже объясню, почему.
✅
Сколько контекста уходит на задачу
Проект у меня живёт на документации в несколько мегабайт markdown'а, и я проектирую через TDD и веду агента строго по докам. Вот примерный бюджет одного задачи по моим прогонам:
цивра в токенах:
30-60k -- агент читает задачу: документация, рамки, ограничения. Это ещё не работа, это понимание.
90-120k -- пробует реализовать рабочую модель: подгружает код, исследует, сверяется с доками.
около 200k -- перепроверяет себя, правит, пишет dev-отчёты.
200-256k и более -- когда есть нюансы.
То есть агент не сидит в хвосте окна сутками -- он в него упирается регулярно и по делу. Весь workflow построен так, чтобы работать модульно и по направлениям, но необходимость загрузить большой контекст всплывает часто: документацию в пару мегабайт иначе не охватить.
Именно поэтому к 200k важна каждая десятая токена в секунду -- это не про комфорт, а про сколько часов уйдёт на задачу.
---
Почему вообще тормозит на длинном контексте
MoE -- это когда параметров много, а в генерации участвует лишь малая часть, "эксперты". Здесь 125B параметров и 6B активных. В 16GB VRAM такое не влезает, поэтому эксперты постоянно мигрируют между RAM и GPU туда-сюда по PCIe.
Упор оказался не в вычислениях, а в пропускной способности шины и задержках. GPU простаивает, пока едут веса.
⚡️
Лечится не "считать быстрее", а "перестать ждать". Форк llama.cpp от
GenerelSchwerz (ветка qwen4exp-mtp) кэширует самых востребованных экспертов в VRAM и прячет I/O-латентность за вычислениями. Вычисления быстрее не стали -- мы просто перестали ждать шину. Никакой магии, менеджмент памяти. Ветка живая, под свою карту собрал сам, флаги сверял на месте.
Кэш убирает лишний I/O, но attention он не отменяет. Чем глубже контекст, тем дороже каждый следующий токен и больше вес KV -- поэтому кривая вниз скорости инференса никуда не делась, её сдвинуло вверх. На 240k потолок всё равно около 7 ток/с, и это потолок архитектуры, а не сборки.
Минус сразу, чтобы потом не было сюрпризов. Спекулятивное декодирование (MTP) с таким кэшем не дружит: просадка примерно в половину против ванильного. Спекуляции нужны веса "под рукой", а их как раз в VRAM и нет -- оверхед на проверку draft-токенов съедает весь выигрыш. --spec-type none, и не трогать. Речь именно про эту связку модели и конфига и сборку llama.cpp, не про метод как таковой.
---
Что говорят логи:
Вытащил из llama-swap зависимость скорости от глубины контекста (на текущей сборке):
📈
- Старт (1,7k): 13 → 22,6 ток/с (
+74%)
- 80k: ~10 → ~13 ток/с (
+30%)
- 130–150k: ~8 → ~10 ток/с (
+25%)
- 180–200k: ~6,7 → ~8,2 ток/с (
+22%)
- 230–245k: 5,4 → ~7 ток/с (
+30%)
Вторая цифра любопытнее. За сессию, где на prompt пришлось 209 939 токенов, из кэша пришло 4 797 079. То есть вход агента почти целиком (96%) -- уже посчитанное ранее. Заново генерируется только ответ, а не весь разговор.
И то же самое в накоплении: за 3 часа 56 минут непрерывной работы кумулятивный кэш вырос с 82k токенов до 245k. Никаких дежурных перезапусков, скорость по мере роста счётчика не просела. Вот это и есть ответ на вопрос "а стабильно ли это" -- не словами, а цифрами.
---
📖
Дальше -- под кат: из чего именно сложилось ускорение, конфиг, ссылки
Три вещи, каждая вкладывает по-своему:
1) --ple-prefetch + --decode-overlap / --decode-boundary-overlap
Асинхронный пайплайн: пока GPU считает текущий токен через attention и активных экспертов, CPU параллельно подкачивает веса под следующий шаг и гонит их по PCIe. Идея пришла из HPC, здесь она адаптирована под MoE-архитектуру.
2) --moe-expert-cache-size
Топ-K реально востребованных экспертов живут в VRAM, остальное подгружается лениво. Меньше обращений к шине на каждый сгенерированный токен.
3) --load-mode none -- отказ от mmap
Самый непропорционально важный флаг из трёх. На многогигабайтных GGUF-шардах mmap при рандомном доступе к экспертам тонет в page faults и системных вызовах. Без mmap (вместе с grouped decode) чтение становится линейным и предсказуемым. Проверено: 128GB RAM для этого достаточно.
И наблюдение в тему, почему на коде быстрее. Роутинг в MoE детерминирован: набор экспертов зависит от входных данных. Код, документация и логи -- материал повторяющийся, активные эксперты почти не меняются, и кэш остаётся горячим. На свободной болтовне набор расползается, и выгоды меньше. Поэтому на кодовой базе модель ощущается быстрее, чем в переписке.
Ссылки:
- https://github.com/GenerelSchwerz/llama.cpp/wiki
- https://github.com/GenerelSchwerz/llama.cpp/wiki/MoE-Cache-Flags-and-16GB-Setup
- https://github.com/GenerelSchwerz/llama.cpp/wiki/PLE-Prefetch-and-Decode-Overlap#trying-the-flags
- https://unsloth.ai/docs/models/qwen3.8-next#mtp-llama.cpp-guide
Рабочий конфиг (16GB VRAM, ветка qwen4exp-mtp):
# ── Qwen3.8-Flash-Next 125B A6B (MoE) - TUNED for 16GB VRAM ─────────────
"qwen3-8-125b-flash-next-tuned":
name: "Qwen3.8-Flash-Next 125B A6B (MoE Cache Tuned)"
description: "256k context with MoE expert cache for 16GB VRAM"
macros:
"ctx-size": "262000"
"ngl": "999"
"offload-rule": "exps=CPU"
"threads": "12"
"batch": "512"
"ubatch": "512"
"kv-type": "q8_0"
"parallel": "1"
"moe-cache-size": "28" # Консервативный старт: 24 слота на тензор (достаточно для K экспертов)
"load-mode": "none" # КРИТИЧНО: отключает mmap, сохраняет grouped decode (128GB RAM достаточно)
cmd: |
${llama-bin-tuned}
${common-flags}
-ot ${offload-rule}
--model ${models-dir}/unsloth/Qwen3.8-Flash-Next-GGUF/Qwen3.8-Flash-Next-UD-IQ4_XS-00001-of-00003.gguf
--cache-type-k ${kv-type}
--cache-type-v ${kv-type}
--cache-prompt
--cpu-moe
--lazy-mode on
--spec-type none
--decode-overlap
--decode-boundary-overlap
--ple-prefetch
--parallel ${parallel}
--ctx-size ${ctx-size}
--n-gpu-layers ${ngl}
--threads ${threads}
--threads-batch 12
--batch-size ${batch}
--ubatch-size ${ubatch}
--flash-attn on
--temp 1.0
--top-k 20
--top-p 0.95
--min-p 0.0
--repeat-penalty 1.0
--presence-penalty 0.0
--frequency-penalty 0.0
--load-mode ${load-mode}
--moe-expert-cache-size ${moe-cache-size}
-fit off
checkEndpoint: /health
aliases:
- "qwen38-flash-next-tuned"
metadata:
architecture: "qwen4exp"
quantization: "UD_IQ4_XS"
ctx_size: ${ctx-size}
purpose: "Reasoning, coding, multi-step tasks (MoE Cache Tuned)"
thinking_mode: true
---
Выводы 💡
1. У имеющегося железа всегда есть резерв. Ищу его там, где болит, прежде чем считать, что потолок достигнут.
2. 125B MoE на 128GB RAM и 16GB VRAM -- это рабочий инструмент, а не демо "влезло и ладно".
3. 256k контекста подъёмны. Решает архитектура работы с памятью, а не гигагерцы и объём VRAM.
4. Если узкое место -- шина, оптимизация вычислений бессмысленна. Оптимизируй доставку данных.
5. Не принимай дефолт за аксиому: mmap хорош, когда в RAM одна модель, и вреден при рандомном доступе к экспертам.
6. Кэш экспертов раскрывается на повторяющемся контексте. Дай модели предсказуемый материал.
7. Большой контекст -- это не рекорд, а бюджет задачи. Мне 250k нужно не для понтов, а потому что документация иначе не влезет.
📌
@tech_di