Comments 120
Ещё из новостей про llama.cpp:
До сих пор не добавили TurboQuant, но на то есть причина - ощутимая просадка скорости, так как вычисления до сих пор не смогли полноценно перенести на GPU и работа выполняется на CPU. Вместо этого добавили “у нас есть turboquant дома”, а именно Rotate Activations. Это одна из частей турбокванта, вращение активаций через матрицу Адамара. И даже это очень сильно повысило качество квантования KV-кэша в размере Q4_0.
Это улучшение включено по умолчанию, и работает для KV-кэша всех вариантов (q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1). Я прогнал пару тестов Q4_0 на 256к контексте, и сработало не плохо, не хуже чем Q8_0.
Добавили нативную поддержку NVFP4 в GGUF. NVFP4 лучше снижает ошибку квантования чем MXFP4, так как разбивает микроблоки дополнительно в 2 раза, делая их более гранулированными, и используют адаптивный scale factor для этих блоков, повышая точность. Я прогнал пару замеров KLD, и кванты NVFP4 GGUF и правда лучше чем чистые MXFP4.
Обновлять llama.cpp нужно, с какой версии это доступно?
И TurboQuant же влияет на размер контекста, не на качество кэша, нет? У меня уже стоит q4_0 и я бы хотел увеличить размер окна с 64к до 128к (а лучше 256к).
Немного уточнений по результатам:
Concurrency - 1?
Значения в таблице - token_throughput?
Как всегда очень полезная статья, спасибо!
Есть rotorquant.
До сих пор не добавили TurboQuant, но на то есть причина - ощутимая просадка скорости, так как вычисления до сих пор не смогли полноценно перенести на GPU и работа выполняется на CPU.
Весьма спорное утверждение. В ассиметрике K:turbo4 V:turbo3 я имею больше скорость tg и чуть ниже скорость pp, нежели на q8_0
Чьи кванты nvfp4 можете рекомендовать на данный момент? Unsloth пока что только mxfp4 в gguf дают.
Те кто прочитал эту помойку текста: напишите плиз просто команду запуска тк не у всех есть время жевать ненужную информацию
https://huggingface.co/unsloth/Qwen3.6-27B-MTP-GGUF https://huggingface.co/unsloth/Qwen3.6-35B-A3B-MTP-GGUF
MPT версии qwen - генерит в 2 раза быстрее оригинальной
Скачать кванты с именем MTP:
И запускать:
.\llama-server -m "Qwen3.6-27B-UD-Q4_K_XL.gguf" --spec-type draft-mtp --spec-draft-n-max 4
Указать --spec-draft-n-max обязательно, по умолчанию будет сильная просадка скорости.
Тебя в гугле забанили? И ненужной информации здесь нет
Могу дать платочек, чтобы рот вытереть.
Боюсь, хабр всё же, пока ещё, технический ресурс, и тут технологии описываются для технарей. Если Вы хотите тупые команды вбивать в консоль не понимая что они делают, то тогда вы не на том ресурсе информацию ищите.
Может кто знает, а аналог Qwen3.5:9B они делать собираются ?
На 4070 без ti гонял и так и эдак. На moe модели увеличения скорости больше, чем на 10-15 процентов не получил. Задачи агентные в основном, на чистой генерации простыни текста, разумеется, быстрее, чем при частых префиллах контекста.
А вот dense модели буквально ускоряются почти в два раза, но у меня это понеслось не с параметром 4, а с параметром 2 токена. Но речь о том, что трудно в целом использовать в работе, например, модель 27b, которая не влезает полностью в видео, и скорость работы 3.5 или 7 токенов в секунду не сильно принципиальна. Чрезмерно ужатые кванты на порядок тупее хорошей и быстрой moe модели на 35b, которая и так пахала где-то на 40-50 т/с, и mtp даёт минимальный прирост, но не забываем, что на сейчас главный минус - mtp нельзя запустить вместе с vision мультимодальностью у qwen.
В последних версиях мульт модальность работает нормально вместе с MTP
Через VLLM можно. И очень быстро.
Вот например 4bit квант Qwen3.6 27B на RTX 5090 за сотню токенов в секунду выдаёт.
За основу был взят вот этот пример: https://www.reddit.com/r/LocalLLaMA/comments/1sw21op?qwen3627bint4_clocking_100_tps_with_256k_context/
vllm serve Lorbus/Qwen3.6-27B-int4-AutoRound \
--speculative-config '{"method":"mtp","num_speculative_tokens":3}' \
--dtype half \
--max-model-len 128000 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 3 \
--quantization auto_round \
--kv-cache-dtype fp8_e4m3 \
--reasoning-parser qwen3 \
--tool-call-parser qwen3_coder \
--enable-auto-tool-choice \
--host 0.0.0.0 \
--port 8000 \
--compilation-config.cudagraph_mode none \
--trust-remote-code \
--enable-prefix-caching \
--enable-chunked-prefill
Попробовал unsloth/Qwen3.6-27B-MTP-UD-Q3_K_XL — практически никакого прироста не заметил.
Промпт: Напиши простую игру "Угадай число" на языке C. Сделай так, чтобы компьютер загадывал число от 1 до 100, а пользователь пытался его угадать.
❯ llama-cli -m Qwen3.6-27B-MTP-UD-Q3_K_XL.gguf --spec-type draft-mtp --spec-draft-n-max 4[ Prompt: 83.3 t/s | Generation: 19.0 t/s ]
Без MTP:
❯ llama-cli -m Qwen3.6-27B-MTP-UD-Q3_K_XL.gguf
[ Prompt: 150.0 t/s | Generation: 18.5 t/s ]
А на unsloth/Qwen3.6-35B-A3B-MTP-UD-Q4_K_XL прирост с 40 до 47 т/с.
Конфигурация: Linux, llama.cpp-vulkan b9190, AMD Radeon RX 9600 XT 16GB, 32 GB RAM.
Всё-таки удалось добиться увеличения скорости с 18 до 31 т/с со следующими параметрами (взял со страницы модели на HuggingFace): llama-cli -m Qwen3.6-27B-MTP-UD-Q3_K_XL.gguf --spec-type draft-mtp --spec-draft-n-max 4 -ngl 99 -fa on -np 1. Видимо, помог перенос всех слоёв модели на GPU (-ngl 99), чего по умолчанию не делалось. Но для этого модель должна полностью вмещаться в VRAM.
Поправьте, пожалуйста, где я неправильно думаю:
Qwen3.6-27B - дофига параметров, но медленная
Qwen3.6-35B-A3B - только 3B активных, поэтому потупее. Зато быстрая
MTP дает ощутимый выигрыш на 27B и существенно меньший на MoE. Т.е. разница в скорости нивелируется, а преимущество в качестве за счет реально используемых 27B параметров остается. В каких случаях тогда стоит 35B-A3B все-таки использовать?
Ну и вопрос про Nemotron-Cascade-2, если можно. Он, насколько я понял, исключительно эффективно работает с контекстом. Увеличение хоть до 100К VRAM отжирает буквально несколько сот мегабайт, а не гигабайты. Разумеется, начальный промпт от какого-нибудь Claude Code несколько минут обрабатывает, но потом на обычные запросы отвечает вполне прилично. Подвохи обнаруживает и т.п. В Qwen тоже можно большой контекст иметь?
MoE имеет смысл использовать когда модель не влезает полностью в видеопамять, тогда часть активных слоев можно переложить на GPU и падение производительности будет не такое большое как в случае dense модели
Да, я так и гонял. start_time,end_time,duration,gpu,author,model,size_gb,ctx,pp_ts,tg_ts,vram_peak,vram_pct,sys_ram_peak,flags
20:38:47,21:06:29,1661.9,3090,nvidia_Nemotron-Cascade-2-30B-A3B-GGUF,nvidia_Nemotron-Cascade-2-30B-A3B-Q5_K_L.gguf,24.51,32768,20.09,11.46,24202,98.5,40874,Full_GPU_Normal: -ctk q8_0 -ctv q8_0 21:06:29,21:33:31,1622.2,3090,nvidia_Nemotron-Cascade-2-30B-A3B-GGUF,nvidia_Nemotron-Cascade-2-30B-A3B-Q5_K_L.gguf,24.51,32768,20.59,11.02,24208,98.5,40264,MoE_1_Expert_CPU: -ctk q8_0 -ctv q8_0 -ncmoe 1 21:33:31,21:37:55,264.3,3090,nvidia_Nemotron-Cascade-2-30B-A3B-GGUF,nvidia_Nemotron-Cascade-2-30B-A3B-Q5_K_L.gguf,24.51,32768,133.4,113.23,24220,98.6,39667,MoE_2_Experts_CPU: -ctk q8_0 -ctv q8_0 -ncmoe 2 21:37:55,21:42:20,265.0,3090,nvidia_Nemotron-Cascade-2-30B-A3B-GGUF,nvidia_Nemotron-Cascade-2-30B-A3B-Q5_K_L.gguf,24.51,32768,133.06,113.45,24220,98.6,39683,MoE_3_Experts_CPU: -ctk q8_0 -ctv q8_0 -ncmoe 3
Разница - небо и земля. При Q5_K_XL, в 24GB не влезает, с 11 до 113 tps выросло при ncmoe 2. Но активных экспертов осталось мало, т.е. по моему пониманию это "скорость вместо качества". Глубокие размышления это исправляют, но тогда и реальная скорость падает (ибо "токенов размышления" больше реального выхлопа, т.е. если поделить размер финального чистого ответа на общее потраченое время, получается 30-40 tps).
Поэтому я и думаю, может 26B сделает сразу качественно. tps формально будет ниже, чем 3B активных, но на итоговый результат столько же потратится благодаря MTP.
start_time,end_time,duration_sec,gpu,model,context,pp_ts,tg_ts,vram_peak,flags
18:27:54,18:28:43,49.0,RTX 3090,Qwen_Qwen3.6-27B-Q4_K_M.gguf,32768,1099.51,36.8,18291,Q8 Cache
18:28:43,18:30:14,91.1,RTX 3090,Qwen_Qwen3.6-27B-Q4_K_M.gguf,65536,882.96,35.89,19507,Q8 Cache
Пока правда не получилось разогнать с MTP...
Но активных экспертов осталось мало, т.е. по моему пониманию это "скорость вместо качества"
Количество экспертов не меняется. Меняется место выполнения (gpu/cpu). Т.е. cmoe на качество работы модели не влияет.
27b существенно лучше qwen3.6 35b или nemotron cascade 2 (кстати, контекст на ней хоть и меньше, но все еще внушительный. за маленьким контекстом в q4 обновленной лламе).
Да, я понимаю, что при той же модели те же веса/слои.эксперты будут обсчитаны.
Вопрос именно в том, что с MTP (якобы) можно иметь "активных" параметров раз в 5 больше (27B vs 3b) с приличным повышением качества ценой уже не пятикратного падения скорости, а раза в 2-3 всего. Что для задач с самопроверкой зачастую выдает финальный результат даже быстрее.
Про Nemotron - мы же говорим про "упихать в досутпную память". Миллионный контекст все равно не получится задействовать даже если теоретически какая-то модель и позволяет. А у немотрона за счет его мамбы можно практически весь его доступный контекст задействовать, и при этом не выгружать дполнительные слои на CPU.
Да, в Claude Code первый запрос приходится несколько минут ждать (если первоначально надо 25К токенов системного промпта обработать), зато потом он кешированый и уже так не тормозит.
Так, MTP не влияет на активные параметры и т.д.
MTP есть и для dense медленной и качественно модели qwen3.6 27b и MTP есть для быстрой и менее сообразительной moe модели qwen3.6 35b.
Надо понимать что MTP ускоряет генерацию токенов, но не ускоряет парсинг промпта. Последнее останавливает меня от использования 27b в агентских задачах, где контекст может быть очень большим.
Рекомендую просто пробовать все варианты и искать тот что вам больше подходит.
Поведения с кешированием промпта валидно для любой модели. Тормозить может и дальше, когда модели нужно будет еще 20к исходников прочитать например.
ну MTP не прям таки нивелирует разницу в скорости. Например, на Ampere картах разница между 27B и 35B примерно в 4 раза в фазе декода. А MTP даёт в лучшем случае х2
В случае с квенами MoE-модель у них если и тупее, то не сильно. Для Claude Code и локальных моделей надо в settings.json указать в "env"(если нет то добавить) "CLAUDE_CODE_ATTRIBUTION_HEADER" : "0"(через export не работает) - так существенно снижается время разработки. Один и тот же промт у меня разогнался с 1:30 до 30 минут
Этот аттрибут отключает специфичный для claude заголовок, из-за которого промпт-кэш сбрасывается постоянно, на собственно скорость работы модели никак не влияет, просто ей каждый раз приходится весь промпт с нуля перерасчитывать. Полный набор какой-то добрый человек выкладывал в реддите:
export CLAUDE_CODE_ATTRIBUTION_HEADER=0
export DISABLE_TELEMETRY=1
export DISABLE_COST_WARNINGS=1
export CLAUDE_CODE_ATTRIBUTION_HEADER=0
export CLAUDE_CODE_DISABLE_1M_CONTEXT=1
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=120000
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=95
export DISABLE_PROMPT_CACHING=1
export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
#export CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1
#export MAX_THINKING_TOKENS=0
export CLAUDE_CODE_DISABLE_FAST_MODE=1
#export DISABLE_INTERLEAVED_THINKING=1
#export CLAUDE_CODE_MAX_RETRIES": "3",
export CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1
export CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=1
export ENABLE_TOOL_SEARCH=auto
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
Что насчет gemma 4? Так и не смог выяснить актуальную инфу по поддержке, гугл выкладывал MTP модели:
https://huggingface.co/google/gemma-4-26B-A4B-it-assistant
Но у unsloth ни квантов ни постов про поддержку этого, поддержки в основной ветке llamacpp кажется тоже нету. Есть ли форки с этим? И есть ли вообще смысл тут в mtp?
Поддерживается в ik_llama.cpp, заметное ускорение дает (при инфренсе на CPU во всяком случае). Это не самостоятельная модель, так что для любой gemma4 подходит, не обязательно от unsloth ждать.
P.S. на CPU и Qwen3.6-35B-A3B заметно быстрее стал, раза в 1.5 (в ik_llama.cpp).
P.P.S. избегайте unsloth, их кванты не могут похвастаться хорошим знанием русского, особенно в последнее время.
на счет плохой поддержки русского в последнее время на unsloth моделях - я не заметил такого
В частности это видно по многословности ответов. Qwen славится своими краткими ответами (даже в рассуждающем режиме), но с UD-Q4 ответы в разы длиннее. На явных ошибках в ответах поймать сложнее, но тоже можно.
Так что мой текущий выбор - bartowski. Очень неплохо себя показывают относительно unsloth с UD или mudler с APEX (но с APEX игрался меньше).
А что мешает ограничить thinking budget, если вам так мешает "многословность". И не может быть так что от смены автора квантовой меняется поведение в thinking режиме
Там не просто квантование, там тюнинг под бенчмарки происходит (и некие “популярные сценарии использования”). Результаты в бенчмарках выше даже чем у оригинальной модели бывают. Ну а все прочее в просадке (место под кванты лимитировано же).
В итоге модель тупее, чем есть, но на бенчмарках просто топ.
P.S. thinking budget не поможет. Наоборот, высокий бюджет на размышления позволяет скрывать огрехи модели.
P.P.S. Я вообще в не-думающем режиме обычно использую - “размышления” редко когда действительно на пользу идут.
Какой тюнинг под бенчмарки?))) Кто вам это сказал?)) Там есть imatrix на котором делались динамические кванты. Никакого тюнинга под бенчмарки там нет))
P.S. thinking budget не поможет. Наоборот, высокий бюджет на размышления позволяет скрывать огрехи модели.
P.P.S. Я вообще в не-думающем режиме обычно использую - “размышления” редко когда действительно на пользу идут.
"Вы или крестик наденьте или трусы снимите" - вы же выше писали что хорошо когда мало думает, а теперь пишете обратное))
Я сказал “хорошо когда модель немногословна”. Если модель многословна - значит что-то пошло не так. Показательно если сравнивать одну и туже модель, но в разных квантах.
Размышления помогают скрывать проблемы с квантом - это да. Но лично я предпочитаю брать нормальный квант, которым размышления редко когда действительно нужны. Если без размышлений модель превращается в тыкву - ну что же, у вас проблемы.
P.S. Да, “тюнинг под бенчмарки” - это мое мнение. Официально там imatrix и динамические кванты (как и у bartowski, к слову). Но видать что-то пошло не так и топовые результаты в бенчмарках еще не все, что надо. Потому как у bartowski работает отлично, а у unsloth наблюдаю проблемы.
P.P.S. Лично я не могу объяснить результаты вида “в бенчмарках лучше оригинала” кроме как тюнингом (где-то встречалось и такое).
Берем обе версии Бартовски и Анслот например Q4_K_M и смотрим что же они там наквантовали и видим почему Анслот выигрывает, а не опираемся на какое-то субъективное мнение... У них attention например существенно лучше квантуется и многое другое... Просто сравните "в лоб". Я не знаю что вы там за проблемы у анслота нашли - я проблем не наблюдаю
Вот как раз Q4_K_M от bartowski и сравнивал с UD-Q4_K_XL (Qwen3.6-35B-A3B). Лоб-в-лоб.
На одних и тех же исходных данных, у unsloth ответы длиннее раза в 2-3 получались. А если в “не-думающем” режиме запускать, то unsloth мог и не справиться с задачей вовсе (оставлял кучу комментариев “что тут делается”, но без реального кода).
gemma-4-26B-A4B показала схожие результаты (разве что в не-думающем режиме такой просадки не увидел, да и в целом просадка ниже была).
Да Qwen3-Coder-30B-A3B обскакал обе, хотя сильно старее. Причем использовался UD-Q4_K_XL от unsloth - т.е. раньше такого не было (или было менее явно).
Убедили, спасибо. Проверим. Жаль, что он еще не выпустил модель с MTP
bartowski вчера выложил mtp-модели как отдельный GGUF-файлы (для указания через --spec-draft-model в llama.cpp). В ik_llama.cpp не завелось, может еще добавят поддержку…
Вообще, MTP-слои можно “пересаживать” между моделями (скрипт встречал для этого), может и тут удастся объединить…
P.S. почитал комментарии - bartowski скорее всего перевыложит оригинальные GGUF-файлы с MTP. С отдельной моделью выше накладные расходы.
Делал несколько десятков тысяч запросов к последним Квен и Гемме под квантами от Unsloth. Никаких проблем с русским не заметил. Обе модели прекрасно работают и четко следуют промпту. Но ниже Q4 обычно не опускаюсь, там уже артефакты сжатия мешают.
Поэтому полностью отказался от Бартовски, хотя он делает отличную работу.
Вы документально можете подтвердить вашу теорию о проблемах Unsloth? Тикет открывали?
У него сколько не проблемы с русским (такое я только на UD-Q2 встречал), а скорее проблемы с качеством работы модели на русском.
Нет, тикетов не открывал. Для грамотного подтверждения теории надо серьезное исследование провести, на что совершенно нет времени:
Какие кванты какие результаты дают? А от других квантовщиков?
А точно именно русский дает такие проблемы или это область знаний виновата?
А какие языки подвержены? А какие темы вызывают проблемы?
А какие параметры запуска использовались и что за софт? А что с другими параметрами или другим софтом?
А какой системный промт использовался? А если другой?
Т.е. нужен полноценный бенчмарк, нормализация результатов и т.д. Сейчас же просто есть пример, где явно LLM не справляется на достаточно простой задаче. При этом выдавая гораздо более многословный ответ, который я и считаю индикатором проблемы для менее показательных случаев.
Да, у меня еще были подобные случаи, но я не копал их глубоко - или перегенерировал результат, или переключался на другую модель, или включал мышление.
Но именно очень ярко проблема всплыла у unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_XL в не-думающем режиме. К той же gemma4 вопросов по существу и нет, кроме “немного более многословный ответ”.
А есть сравнение с vLLM, sglang или на чем там оригинальные веса работают? Речь про то, что Qwen выкладывает веса в формате, который нельзя запустить в llama.cpp, зато можно в vLLM и других программах. И в них тоже есть multi-token prediction (в vLLM вроде можно даже указать, сколько токенов предсказывать, 2 или 3). Учитывая, что vLLM запускается несколько минут, подсчитывая какие-то там CUDA graph, она может работать быстрее, чем llama.cpp.
Я сравнивал у себя на RTX 4090 D (48 Гб памяти VRAM).
Детали и конфиги вот здесь:
https://huggingface.co/unsloth/Qwen3.6-27B-MTP-GGUF/discussions/25
Llama.cpp UD-Q6, без MTP: 31 токен в секунду
Llama.cpp UD-Q6 и MTP: 60 токенов в секунду. Используется дополнительные 2 ГБ VRAM. Prompt processing замедляется на 20%
VLLM и MTP: 40-48 токенов в секунду. В 2 раза быстрее prompt processing.
Qwen 3.5 9b на 3060 улучшение с 47 ток/с до 60
Это на каком размере контекста?:)
Тоже карта 3060 12gb, ram 32gb, Linux.
Лучше юзать новые версии Qwen:
Unsloth Qwen3.6-35B-A3B-UD-Q4_K_XL
они хоть и медленнее 20-40 t\s, но сам ответ качественнее, а то прошлые часто ходят вокруг да около и переделают по много раз.
Новые с первого раза или через пару итераций.
В LM-Studio можно такие параметры выставить для большей скорости:

1. если вылетает от нехватки VRAM увеличивайте на 1 и снова проверяйте.
2. при использовании внутри vscode увеличить до 4
Число экспертов 6
FP8
Будет больше 40t\s)
Но конечно потеряем в качестве
del
У меня на модели qwen3.6 27b Q8_K_XL от unsloth наибольшую прибавку дает запуск с параметрами:
--spec-type draft-mtp --spec-draft-n-max 6 \
--spec-draft-p-min 0.75 \Имея конфигурацию только из CPU (i7-12700) + RAM 32ГБ, есть смысл пробовать что-то выжать из MTP моделей?
Да, Qwen3.6-35B-A3B / gemma-4-26B-A4B очень хороши. От MTP выигрыш на 30-50% возможен (gemma-4 с assistant аналогично). Плюс сам софт оптимизируют.
Для чата скорости хватает, но для работы агента слабовато будет.
"очень хороши" - сколько токенов секунду?
Не могли бы пожалуйста поделиться результатами из llama-bench с параметрами? --n-prompt 4096 --n-gen 4096
Результаты для ik_llama.cpp, но без mtp/assist ускорения (оно дает еще 30-50% на TG). В llama.cpp PP ниже раза в 2, TG примерно одинаковый будет.
gemma-4-26B-A4B
$ llama-bench -m models/bartowski_google_gemma-4-26B-A4B-it-Q4_K_M.gguf --n-prompt 4096 --n-gen 4096
| model | size | params | backend | threads | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | ------: | ------------: | ---------------: |
llm_scale_gate_inp_s: n_embd = 2816
| gemma4 ?B Q4_K - Medium | 16.41 GiB | 25.97 B | CPU | 8 | pp4096 | 69.98 ± 3.24 |
| gemma4 ?B Q4_K - Medium | 16.41 GiB | 25.97 B | CPU | 8 | tg4096 | 8.43 ± 0.10 |
build: 40aae0b6d (4509)
Qwen3.6-35B-A3B
$ llama-bench -m models/bartowski_Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf --n-prompt 4096 --n-gen 4096 -r 1
| model | size | params | backend | threads | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | ------: | ------------: | ---------------: |
| qwen35moe 35B.A3B Q4_K - Medium | 19.91 GiB | 34.66 B | CPU | 8 | pp4096 | 88.11 ± 0.00 |
| qwen35moe 35B.A3B Q4_K - Medium | 19.91 GiB | 34.66 B | CPU | 8 | tg4096 | 11.42 ± 0.00 |
build: 40aae0b6d (4509)
P.S. “очень хороши” - это, в первую очередь, личная оценка качества работы при приемлемой скорости.
Отлично! Спасибо за результаты!
А для моделей mtp есть возможность сделать ровно такие же тесты? Чтобы как раз и посмотреть разницу)
Кстати для полной картины можно и для llama.cpp прикрепить, чтобы увидеть просадку на PP
P.S. В прошлой статье вроде Shannon рекомендовал использовать UD_Q4_K_XL для llama.cpp (не ik). Может на них тест PP будет норм?
MTP не смог задать - нет нужных флагов (может что криво смотрел). Не уверен, что там вообще MTP применим - если тест просто рандом генерирует, то MTP не даст ускорения. Тем более, что MTP дает разные результаты для разного типа контента.
Для llama.cpp тест с gemma-4-26B-A4B дал 39.85 / 8.15 (PP / TG), а для Qwen3.6-35B-A3B llama.cpp упал )
P.S. я не сторонник unsloth - на мой взгляд, они сильно агрессивно подходят к квантованию отдельных слоев, из-за чего качество на русском падает.
P.S. В прошлой статье вроде Shannon рекомендовал использовать UD_Q4_K_XL для llama.cpp (не ik). Может на них тест PP будет норм?
ik_llama.cpp как раз оптимизирован для работы на CPU. UD не даст существенной разницы, скорее даже хуже работает (как понимаю, из-за очень разного квантования разных слоев - местами накладных расходов получается больше). Но это субъективные ощущения, целенаправленных тестов не делал - с момента теста софт обновился и заметно ускорился, сейчас может и лучше.
У меня примерно следующие результаты (в целом +-как у Вас). ik_llama показывает себя лучше только в PP
Скрытый текст
Модель: gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf Общие параметры тестирования: контекст 4096 (--n-prompt 4096), генерация 4096 (--n-gen 4096)
1. Стандартный llama-bench (сборка 8951)
Параметры:
-r 1и-r 5(1 и 5 прогонов)Обработка промпта (pp4096): ~59.4 t/s
Генерация токенов (tg4096): ~9.75 t/s
2. Форк ik_llama-main (сборка 4660)
Параметры:
-r 1(2 одинаковых прогона)Обработка промпта (pp4096): 94.53 t/s
Генерация токенов (tg4096): 10.50 t/s
Если согласны потерять сколько-то в качестве, то можно ускориться раза в 1.5. Попробуйте кванты, которые хорошо работают на CPU, это Q4_0 и Q4_1.
Так это для ARM нужно в основном - там нет оптимизаций для Q4_K_M. При инференсе на CPU все равно упирается в пропускную способность памяти (но не всегда, это да, особенно если не все ядра использовать для инференса).
Про Q4_1 bartowski пишет: Legacy format, similar performance to Q4_K_S but with improved tokens/watt on Apple silicon.
А вот что про Q4_0: Legacy format, offers online repacking for ARM and AVX CPU inference.
P.S. я ограничился 4 ядрами для инференса и дефолтными 8 для обработки запроса. В TG немного проиграл, но сильно выиграл в уровне шума ) PP уже заметно чувствительнее к количеству ядер.
Я путем многих тестов (скрипт на питоне, где задавал написать эссе с определенной длиной и мерил скорость), выяснил что для i7-12700 (8 производительных ядер) оптимально всё таки -t 8 -tb 8. Разница на тяжелых моделях примерно 1-1.5 t/s, но в цифрах это по сути 10%)
Да, так оно и есть. Но уровень шума оказался решающим ) Шумит может и не сильно, но его слышно.
Я бы поменял и кулер, и просто вертушек бы добавил, но у меня не полноценный системник, а ультракомпактный мини-ПК (или MFF — Micro Form Factor), объемом где-то в 1 литр стоит на столе ) В нем охлаждение скорее ноутбучное, хотя декстопный i7 стоит. Я даже Turbo Boost выключил - поберечь и уши, и камень.
Попробовал MTP для Qwen3.6-35B-A3B-Q4_K_M.gguf на CPU в llama.cpp - производительность даже ниже, чем без MTP. При этом в ik_llama.cpp наблюдаю заметный выигрыш в скорости (на 30-50%).
Может что в llama.cpp уже сломали (использую свежий самосбор с main-ветки), но что-то явно пошло не так.
Хотел еще уточнить, с такими небольшими скоростями какой софт ещё используете помимо ik_llama? Как применяете модельки локальные если не секрет?
В основном в чате переписываюсь. Поэтому часто запускаю llama.cpp - у нее интерфейс немного приятнее, а длинные диалоги все равно стараюсь не вести )
Дополнительно отключил мышление (через Custom JSON config или флагами запуска) - процент задач, где оно на пользу очень небольшой, а нагрузку дает существенную. Да и быстро можно остановить генерацию, если модель “ушла не туда”, или дала ответ в начале и продолжила предлагать варианты (с мышлением тоже можно остановить, но сложнее понять, когда модель явно ушла не туда без финального ответа).
Стоит еще OpenWebUI, но больше для внешнего провайдера LLM с оплатой по токенам.
Агентские сценарии пробовал, но не очень зашло. Локально очень долго работает, особенно учитывая системные промты и описания инструментов на десятки тысяч токенов. А внешний провайдер вызывает вопросы приватности.
Как бы можно оставить агента и уйти по делам, но мне агентская разработка в целом как-то не очень зашла (даже с внешним провайдером) - несколько раз пробовал, очень сомнительные результаты получил. Правда это было не на самых топовых моделях.
Как применяете модельки локальные если не секрет?
Основная модель сейчас - Qwen3.6-35B-A3B и gemma-4-26B-A4B. Qwen для работы с кодом и технических вопросов, gemma для работы с текстом (переводы, перефразировать и прочее).
В gemma-4 заметно прокачали технические навыки, но все-таки до Qwen3.6 не дотягивает, на мой взгляд. Qwen3.6 лучше следует инструкции и в целом более адекватен при работе с кодом.
Раньше в основном использовал Qwen3-Coder-30B-A3B и линейку Qwen3-30B-A3B в целом (и gemma-3-4B-it для текста).
P.S. зачастую запущена одна модель и на ней решаю вопросы. Если вижу, что результат “не очень” и это не “профиль” модели, то переключаю.
Бесит, что в ik_llama нельзя модели сразу все загрузить и режиме роутера переключатся.
Мне кстати для документации/инструкций больше нравится T-lite-2.1. Модель реально хороша адаптирована под русский язык. Очень часто именно она дает те ответы, которые мне нужно. Причем с правильной логикой и хорошим обоснованием.
Еще желательно ей промт задать, что то типа:
Скрытый текст
Ты профессиональный ассистент и инженер.
Твоя задача — давать точные, полезные и практичные ответы на любые запросы: анализ текста, документации, код, объяснения и решение задач.
Правила:
Отвечай структурировано и по делу
Избегай воды и общих фраз
Делай акцент на практическом применении
Не выдумывай факты, если информации нет — прямо укажи это
Упрощай сложные вещи, но не теряй смысл
При необходимости делай выводы и давай рекомендации
Если пользователь прислал текст или документацию:
кратко объясни суть
выдели ключевые моменты
покажи, как это использовать на практике
Если вопрос технический:
давай конкретные шаги или код
указывай важные параметры и настройки
предупреждай о типичных ошибках
Если задача аналитическая:
выделяй главное
структурируй информацию
делай выводы
Формат ответа:
четкий, логичный, без лишнего текста
при необходимости используй списки и шаги
Можно запустить несколько экземпляров ik_llama.cpp на разных портах ) Так и llama.cpp делает, разве что позволяет к разным моделям обращаться через свой UI.
Если критичен режим “одно окно” - то в OpenWebUI можно подключить сколько угодно ik_llama.cpp и выбирать нужную модель “на лету”.
P.S. с T-lite-2.1 не игрался. 7B заметно медленнее будет A3B-моделей. Максимум c GigaChat3.1-10B-A1.8B пробовал играться, но она явно уступала Qwen3-30B-A3B.
А где можно посмотреть графики, как меняется "хорошесть разных моделей" в зависимости от квантизации и количества параметров?
Это ошибка так ориентироваться. Надо взять свои собственные привычные сценарии использования и на них прогнать. Вот и поймете. Я так по своим задачам вынужденно сделал выбор в пользу qwen 3.6 moe против Gemma 4 26b, потому что именно в конкретных задачах квен выдала просто идеальный результат (создание документов по образцам, написание договора, due diligence контрагента по всяким апи и реестрам в тырнете, напиисание протоколов, решений в корпоративной сфере).
А нет примеров как вы проводите эти тесты?
Не кидаете же просто промпт и потом на глазок результат сравниваете? Или так и происходит? :)
Сами документы - на глазок, а точнее на корректность выполнения по сути, проверяю глазами, да. Но технически также сравниваю скорость, объем раздумывания, смотрю, что модель там в своих размышлениях делала. Строго ли следовала инструкциям. Грузила ли нужные инструменты в правильной последовательности.
Unsloth и Бартовски выкладывают тесты хорошести квантов. Вот например https://unsloth.ai/docs/models/qwen3.6#benchmarks
Сейчас сделал тест с MTP и без на Qwen3.6-35B-A3B. По крайней мере у Unsloth я бы не использовал MTP и вот почему:
Запрос: "Вопрос: "Моя машина грязная и я хочу её помыть.Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: 'доехать' или 'прогуляться'." Дай ответ на этот вопрос и оценку точности ответа, где 1.0 - абсолютно точно, 0.0 - абсолютно не точно и это критерий оценки твоей уверенности в этом ответе. Значения могу лежать в диапазоне от 0.0 до 1.0. Выдача должна быть в JSON со следующими полями: answer, accuracy"
Ответ с MTP:
Скрытый текст
The user is asking a question in Russian. Question: "My car is dirty and I want to wash it. There is a car wash 100 meters from home. How should I proceed: drive there or walk? Answer with one word: 'drive' or 'walk'." Requirement: Answer with 'drive' or 'walk', and provide an accuracy score (0.0 to 1.0). Output in JSON format with fields 'answer' and 'accuracy'. Analysis: If the car is dirty, it needs to be washed. The car wash is only 100 meters away. Driving a dirty car 100 meters to wash it is counter-intuitive and inefficient. It's much closer to walk there. Therefore, the logical answer is 'walk' (прогуляться). Accuracy should be high, close to 1.0. Let's double check the logic. Goal: Wash the car. Location of wash: 100m away. Current state: Car is at home, dirty. Option A: Drive car to wash (100m). This defeats the purpose of washing immediately, wastes fuel, and keeps the car dirty for 100m. Option B: Walk to wash. You can take a bag of cleaning supplies (or just go to the wash where they clean it), or the car wash might offer mobile service, or you just go to the location to arrange it. Actually, usually if the car is dirty, you walk to the car wash to drop it off or wash it yourself. Driving it 100m is silly. So 'прогуляться' is the correct answer. JSON format: { "answer": "прогуляться", "accuracy": 1.0 } Wait, the prompt says "Ответь одним словом: 'доехать' или 'прогуляться'." I will output exactly that. Let's construct the JSON. answer: "прогуляться" accuracy: 1.0 Check constraints: "Выдача должна быть в JSON со следующими полями: answer, accuracy" I will produce just the JSON. Done. Self-Correction/Refinement during thought: Is there any trick? "My car is dirty... car wash 100m away." Usually, if a car is dirty, you don't drive it. You walk to the car wash. Accuracy 1.0 is appropriate. Output matches requirement. Proceed. Output generation. ``json { "answer": "прогуляться", "accuracy": 1.0 } ` Matches perfectly. No extra text. I will generate the response. One minor point: The prompt asks for one word answer in the text, but also asks for JSON output. I will provide just the JSON as requested. "Выдача должна быть в JSON со следующими полями: answer, accuracy" -> Output must be JSON. Ready. Output matches. Proceeds. [Final Check] Question: "Моя машина грязная и я хочу её помыть.Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: 'доехать' или 'прогуляться'." Logic: Car is dirty -> don't drive it -> walk. Answer: прогуляться Accuracy: 1.0 JSON structure correct. Done. Output Generation. `json { "answer": "прогуляться", "accuracy": 1.0 } `` All good. Proceeds. (Self-Correction/Verification during generation) Wait, is there any scenario where driving is better? Maybe if you need to transport cleaning equipment? But 100m is very short. Walking is universally the logical choice for a dirty car nearby. Accuracy 1.0 is safe. The prompt asks for JSON. I will output strictly JSON. Done. Output matches response. Proceeds. [Final Output Generation] -> Proceeds
Ответ без MTP:
Скрытый текст
The user wants me to answer a question about whether to drive or walk to a car wash that is 100 meters away from their house, given that their car is dirty and needs washing. The answer must be a single word: 'доехать' (drive) or 'прогуляться' (walk). I also need to provide an accuracy score (0.0 to 1.0) and output the result in JSON format with fields 'answer' and 'accuracy'. Reasoning: The goal is to wash the car. The car wash is 100 meters away. If I walk to the car wash, I cannot wash the car because the car is at home. Therefore, I must drive the car to the car wash to wash it. Even though it's a short distance (100m), the logical action to get the car washed is to drive it there. Answer: 'доехать' Accuracy: 1.0 (It is logically the only way to wash the car). JSON Output: { "answer": "доехать", "accuracy": 1.0 }
Интересный тест!
Какая квантизация была (как самой модели, так и KV cache)? И сколько раз вы пробовали?
Множественной перегенерацией я сомг добиться "прогуляться", но обычно нормально отвечали.
На Q5_K_XL (RTX 3090 24GB) хоть MTP, хоть просто MoE, хоть unsloth, хоть bartowski - примерно одинаково отвечают

unsloth\Qwen3.6-27B-MTP-GGUF\Qwen3.6-27B-UD-Q5_K_XL.gguf
-ngl 99 -fa on -np 1 -ctk q8_0 -ctv q8_0 -c 65536 --spec-type draft-mtp --spec-draft-n-max 6 -fitt 512
Долго думала, итоговых токенов мало, поэтому Anything LLM пишет всего лишь 1 токен в секунду.
[0m[34m0.55.841.384[0m [32mI [0mslot create_check: id 0 | task 70 | created context checkpoint 1 of 32 (pos_min = 198, pos_max = 198, n_tokens = 199, size = 150.043 MiB)
[34m0.57.419.427[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 103, tg = 68.34 t/s
[34m1.00.438.963[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 222, tg = 49.04 t/s
[34m1.03.478.192[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 324, tg = 42.82 t/s
[34m1.06.507.649[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 468, tg = 44.17 t/s
[34m1.09.570.021[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 597, tg = 43.71 t/s
[34m1.12.606.563[0m [32mI [0mslot print_timing: id 0 | task 70 | n_decoded = 738, tg = 44.21 t/s
[34m1.15.684.694[0m [32mI [0mslot print_timing: id 0 | task 70 |
prompt eval time = 372.88 ms / 203 tokens ( 1.84 ms per token, 544.41 tokens per second)
eval time = 19772.48 ms / 914 tokens ( 21.63 ms per token, 46.23 tokens per second)
total time = 20145.36 ms / 1117 tokens
draft acceptance rate = 0.54829 ( 704 accepted / 1284 generated)
[34m1.15.684.712[0m [32mI [0mstatistics draft-mtp: #calls(b,g,a) = 2 281 281, #gen drafts = 281, #acc drafts = 243, #gen tokens = 1686, #acc tokens = 886, dur(b,g,a) = 0.003, 5283.043, 0.622 ms
[34m1.15.684.755[0m [32mI [0mslot release: id 0 | task 70 | stop processing: n_tokens = 1121, truncated = 0
[34m1.15.684.770[0m [32mI [0mslot print_timing: id 0 | task -1 | n_decoded = 914, tg = 46.23 t/s---

unsloth\gemma-4-26B-A4B-it-GGUF\gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf
-ngl 99 -fa on -np 1 -ctk q8_0 -ctv q8_0 -c 65536 -fitt 512
[34m4.41.968.646[0m [32mI [0mslot print_timing: id 0 | task 297 | n_decoded = 100, tg = 93.33 t/s
[34m4.44.970.286[0m [32mI [0mslot print_timing: id 0 | task 297 | n_decoded = 389, tg = 95.50 t/s
[34m4.47.353.585[0m [32mI [0mslot print_timing: id 0 | task 297 |
prompt eval time = 255.92 ms / 202 tokens ( 1.27 ms per token, 789.30 tokens per second)
eval time = 6456.41 ms / 619 tokens ( 10.43 ms per token, 95.87 tokens per second)
total time = 6712.34 ms / 821 tokens
[34m4.47.353.636[0m [32mI [0mslot release: id 0 | task 297 | stop processing: n_tokens = 820, truncated = 0Ну так нормальные параметры для модели надо ставить, а не дефолтные, тогда и работать будет хорошо
MPT
Скрытый текст
Here’s a thinking process:
Analyze User Input:
Question: “Моя машина грязная и я хочу её помыть. Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: ‘доехать’ или ‘прогуляться’.”
Constraints:
Answer must be exactly one word: either ‘доехать’ or ‘прогуляться’.
Provide an accuracy score between 0.0 and 1.0 representing confidence.
Output must be in JSON format with fields:
answer,accuracy.
Context/Logic: The car is dirty, and the user wants to wash it. The car wash is only 100 meters away. If they drive the dirty car to the car wash, they’ll just drive a dirty car 100m, which is fine, but the point is to wash it. Actually, driving a dirty car to a car wash is standard. But wait, if they walk, how will the car get washed? The car needs to be at the car wash. So they must drive it there. Walking wouldn’t make sense because the car itself needs to be washed. Therefore, the logical answer is ‘доехать’.
Confidence: Very high. It’s a straightforward logical deduction. I’ll assign 1.0.
Formulate Output:
JSON structure:
{"answer": "доехать", "accuracy": 1.0}Check constraints: One word? Yes. Matches options? Yes. JSON format? Yes. Fields correct? Yes. Accuracy in range? Yes.
Self-Correction/Verification:
Could there be a trick? “Моя машина грязная и я хочу её помыть.” -> The car needs washing. Car wash is 100m away. To wash the car, the car must go to the car wash. So driving is necessary. Walking would leave the car dirty at home. So ‘доехать’ is definitively correct.
Accuracy: 1.0 is appropriate given the clear logic.
JSON format: Must be valid. I’ll output exactly that.
All constraints met. Proceed. Output: {"answer": "доехать", "accuracy": 1.0}✅
без MTP
Скрытый текст
Analyze User Input:
Question: “Моя машина грязная и я хочу её помыть. Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: ‘доехать’ или ‘прогуляться’.”
Translation/Meaning: “My car is dirty and I want to wash it. The car wash is 100m from home. What should I do: drive there or walk? Answer with one word: ‘drive’ or ‘walk’.”
Constraints: Answer must be exactly one word: ‘доехать’ or ‘прогуляться’. Output must be JSON with fields
answerandaccuracy(0.0 to 1.0).Task: Provide the answer and an accuracy/confidence score.
Evaluate the Scenario:
The car is dirty and needs washing.
The car wash is only 100 meters away.
If I walk, the car stays dirty. I need to get the car to the car wash to wash it.
Therefore, I must drive the car to the car wash. Walking makes no sense for the stated goal (washing the car).
Answer: ‘доехать’
Determine Accuracy/Confidence:
The logic is straightforward and unambiguous. The goal is to wash the car, so the car must be at the car wash. Driving is the only logical choice.
Confidence should be very high. I’ll set it to 1.0.
Format Output:
Must be JSON with fields:
answer,accuracy.Structure:
{"answer": "доехать", "accuracy": 1.0}
Self-Correction/Verification:
Check constraints: “Ответь одним словом: ‘доехать’ или ‘прогуляться’.” -> Matches.
JSON format required -> Matches.
Accuracy range 0.0-1.0 -> Matches.
Logic sound? Yes. Walking wouldn’t get the car washed. Driving is necessary.
Output matches exactly.
All good. Proceed.
Generate response. {"answer": "доехать", "accuracy": 1.0}✅
Note: I’ll make sure the JSON is valid and matches the exact field names requested. No extra text. The prompt says “Выдача должна быть в JSON со следующими полями: answer, accuracy”. I will output only the JSON.✅
Final check of the prompt: “Дай ответ на этот вопрос и оценку точности ответа… Выдача должна быть в JSON со следующими полями: answer, accuracy” All constraints met. Output matches.✅
Proceed. [Output Generation] -> {"answer": "доехать", "accuracy": 1.0} (Self-Correction/Refinement during thought: Should I include any explanation? The prompt says “Выдача должна быть в JSON…”, so strictly JSON only is safer. I’ll output just the JSON.)✅
Final Answer Generation. {"answer": "доехать", "accuracy": 1.0} Done. [Output matches response]✅
(Self-Correction/Verification during generation): Wait, let’s double check the exact wording. “Ответь одним словом: ‘доехать’ или ‘прогуляться’.” The JSON field answer should contain that word. Accuracy is a float. All good. One minor point: Sometimes prompts like this are trick questions (e.g., if you walk, you can’t wash the car, so obviously drive). It’s straightforward. Confidence 1.0 is appropriate. Output matches exactly.✅
Proceeds. [Final Output Generation] -> {"answer": "доехать", "accuracy": 1.0} (Done.)
Конечный ответ одинаковый !
Нормальные это какие? Подскажите? Потому что у меня не дефолтные))
У меня из того что влияет на ответ - это UD-Q4_K_XL и кэш в обоих случаях поквантован в q8_0. Т.е. условия тестирования одинаковые, а результаты разные, что говорит о том, что MTP все же делает хуже.
Думаю речь про температуру и сэмплинг:
temperature=1.0, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=1.5, repetition_penalty=1.0Следуя последним рекомендациям должно быть
presence_penalty=0
А это интересная тема. Unsloth для агентов рекомендуют 0 что бы не ограничивать модель в повторении токенов, т.к. в агентской истории по написании кода это частое явление. А для обычного чата рекомендуется 1.5
Аналогично с температурой. Для агентов минимально, для обычного общения побольше
https://unsloth.ai/docs/models/qwen3.6
Так не получится сравнить, сделав несколько прогонов, вы оцениваете сэмплинг, а не модель MTP. Модель детерминирована, а сэмплинг стохастичен и сэмплинг это внешняя от модели сущность. Для сравнения надо либо отключить сэмплинг, либо собрать статистику на большом количестве замеров.
Прогнал 100 замеров без MTP и 100 с MTP на вашем задании. Температура 0.8, top_p 0.85.

Вариант с MTP выдал 64% правильных ответов, а без MTP - 57%. Но не стоит цепляться за сами проценты, в следующем прогоне будет наоборот, серия “доехать” может составлять 10 подряд, а потом 5 подряд будет “прогуляться”, один “доехать” и снова 5 “прогуляться”. Главное тут то, что температура 0.8 добавляет слишком много случайности, а top_p 0.85 креативности (значение по умолчанию в 0.95 добавляет ещё больше креативности).
Температура 0.3, процент правильных ответов стремится к 75%:

Температура 0, процент правильных ответов 100%:

Сэмплинг в данном случае вносит слишком много хаоса, модель хоть и может найти правильный ответ на задачу, но внешняя случайность от сэмплинга уводит модель в сторону. MTP тут не при чём, в статье я как раз смотрел исходники llama.cpp чтобы убедиться, что MTP не добавляет от себя ничего и никак не искажает генерацию.
Код скрипта, для тех кто хочет повторить
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Тест-прогоны llama.cpp (v1 + stream)</title>
<style>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin:0; background:#1e1e2e; color:#cdd6f4; height:100vh; overflow:hidden; }
/* ===== Scrollbars ===== */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: #181825; border-radius: 6px; }
::-webkit-scrollbar-thumb {
background: #45475a;
border-radius: 6px;
border: 2px solid #181825;
transition: background .2s;
}
::-webkit-scrollbar-thumb:hover { background: #89b4fa; }
::-webkit-scrollbar-corner { background: #181825; }
/* Firefox */
* {
scrollbar-width: thin;
scrollbar-color: #45475a #181825;
}
.layout { display:flex; height:100vh; }
.main { flex:1; overflow-y:auto; padding:20px; min-width:0; }
.side { width:480px; border-left:1px solid #45475a; display:flex; flex-direction:column; background:#181825; }
.side-header { padding:10px 14px; background:#313244; font-weight:600; border-bottom:1px solid #45475a; }
.side-body { flex:1; overflow-y:auto; padding:12px 14px; font-family: monospace; font-size:12.5px; white-space:pre-wrap; word-break:break-word; line-height:1.5; }
.side-body .thinking { color:#9399b2; font-style:italic; }
.side-body .content { color:#cdd6f4; }
.side-body .sep { color:#89b4fa; margin:8px 0; display:block; border-top:1px dashed #45475a; padding-top:8px; }
h1 { color:#89b4fa; margin-top:0; }
.card { background:#313244; padding:16px; border-radius:10px; margin-bottom:16px; }
label { display:block; margin:8px 0 4px; font-size:13px; color:#bac2de; }
input, button, textarea { padding:8px 12px; font-size:14px; border-radius:6px; border:1px solid #45475a; background:#1e1e2e; color:#cdd6f4; }
button { background:#89b4fa; color:#1e1e2e; font-weight:600; cursor:pointer; border:none; margin-right:8px; }
button:disabled { background:#585b70; cursor:not-allowed; color:#a6adc8; }
.log { font-family: monospace; font-size:12px; background:#11111b; padding:10px; max-height:320px; overflow-y:auto; border-radius:6px; }
.log div { margin-bottom:4px; border-bottom:1px solid #313244; padding-bottom:4px; }
.ok { color:#a6e3a1; } .fail { color:#f38ba8; } .warn { color:#f9e2af; }
table { width:100%; border-collapse:collapse; margin-top:10px; }
th, td { padding:6px 10px; border:1px solid #45475a; text-align:left; }
th { background:#45475a; }
.progress { height:8px; background:#45475a; border-radius:4px; overflow:hidden; margin:10px 0; }
.progress > div { height:100%; background:#a6e3a1; transition: width .2s; }
textarea { width:100%; }
.hint { font-size:12px; color:#9399b2; margin-top:4px; }
.sampling-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:10px; margin-top:10px; }
.sampling-grid label { margin:0 0 4px; font-size:12px; }
.sampling-grid input { width:100%; }
.btn-stop { background:#f38ba8 !important; }
</style>
</head>
<body>
<div class="layout">
<div class="main">
<h1>🧪 Прогоны llama.cpp (v1 / streaming)</h1>
<div class="card">
<label>URL сервера (v1 endpoint):</label>
<input id="url" value="http://localhost:8080/v1/chat/completions" style="width:90%">
<div class="hint">Используется OpenAI-совместимый эндпоинт. По умолчанию <code>/v1/chat/completions</code>.</div>
<label>Количество прогонов в серии:</label>
<input id="count" type="number" value="25" min="1" max="500">
<label>Параметры sampling:</label>
<div class="sampling-grid">
<div>
<label>temperature</label>
<input id="temperature" type="number" value="0.8" step="0.1" min="0">
</div>
<div>
<label>top_p</label>
<input id="top_p" type="number" value="0.85" step="0.05" min="0" max="1">
</div>
<div>
<label>top_k</label>
<input id="top_k" type="number" value="40" step="1" min="0">
</div>
<div>
<label>min_p</label>
<input id="min_p" type="number" value="0.05" step="0.01" min="0" max="1">
</div>
<div>
<label>presence_penalty</label>
<input id="presence_penalty" type="number" value="0.0" step="0.1" min="-2" max="2">
</div>
<div>
<label>frequency_penalty</label>
<input id="frequency_penalty" type="number" value="0.0" step="0.1" min="-2" max="2">
</div>
</div>
<label style="margin-top:16px;">Промпт (user message):</label>
<textarea id="prompt" rows="6" style="background:#11111b;padding:8px;border-radius:6px;border:1px solid #45475a;">Вопрос: "Моя машина грязная и я хочу её помыть. Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: 'доехать' или 'прогуляться'." Дай ответ на этот вопрос и оценку точности ответа, где 1.0 - абсолютно точно, 0.0 - абсолютно не точно и это критерий оценки твоей уверенности в этом ответе. Значения могут лежать в диапазоне от 0.0 до 1.0. Выдача должна быть в JSON со следующими полями: answer, accuracy</textarea>
<div style="margin-top:12px;">
<button id="btn1">▶ Начать прогон "Без MTP"</button>
<button id="btn2">▶ Начать прогон "С MTP"</button>
<button id="btnStop" class="btn-stop" style="display:none;">⏹ Остановить</button>
<button id="btnReset">🔄 Сброс</button>
<button id="btnClearStream" style="background:#f38ba8;color:#1e1e2e;">🧹 Очистить панель</button>
</div>
<div class="progress"><div id="bar" style="width:0%"></div></div>
<div id="status" style="margin-top:6px;"></div>
</div>
<div class="card">
<h3>📊 Статистика</h3>
<table id="stats">
<tr><th>Метрика</th><th>Без MTP</th><th>С MTP</th></tr>
<tr><td>Всего запросов</td><td id="s1_total">—</td><td id="s2_total">—</td></tr>
<tr><td>Валидный JSON</td><td id="s1_valid">—</td><td id="s2_valid">—</td></tr>
<tr><td>Ответ "доехать"</td><td id="s1_drive">—</td><td id="s2_drive">—</td></tr>
<tr><td>Ответ "прогуляться"</td><td id="s1_walk">—</td><td id="s2_walk">—</td></tr>
<tr><td>Правильных ответов (%)</td><td id="s1_correct_pct">—</td><td id="s2_correct_pct">—</td></tr>
<tr><td>Ошибок JSON</td><td id="s1_err">—</td><td id="s2_err">—</td></tr>
</table>
</div>
<div class="card">
<h3>📝 Лог</h3>
<div id="log" class="log"></div>
</div>
</div>
<div class="side">
<div class="side-header">💬 Поток ответа модели</div>
<div id="stream" class="side-body"><span style="color:#6c7086;">Здесь в реальном времени появится генерация…</span></div>
</div>
</div>
<script>
const logEl = document.getElementById('log');
const statusEl = document.getElementById('status');
const bar = document.getElementById('bar');
const streamEl = document.getElementById('stream');
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
const btnStop = document.getElementById('btnStop');
const btnReset = document.getElementById('btnReset');
const btnClearStream = document.getElementById('btnClearStream');
let isRunning = false;
let currentController = null;
let stats1 = { total: 0, valid: 0, drive: 0, walk: 0, accSum: 0, accCount: 0, latSum: 0, err: 0 };
let stats2 = { total: 0, valid: 0, drive: 0, walk: 0, accSum: 0, accCount: 0, latSum: 0, err: 0 };
function addLog(msg, cls='') {
const d = document.createElement('div');
d.className = cls;
d.textContent = msg;
logEl.prepend(d);
}
function setStatus(t) { statusEl.textContent = t; }
function setProgress(p) { bar.style.width = p + '%'; }
function streamStart(reqIndex) {
const sep = document.createElement('span');
sep.className = 'sep';
sep.textContent = `── запрос #${reqIndex} ──`;
streamEl.appendChild(sep);
streamEl.scrollTop = streamEl.scrollHeight;
}
function streamChunk(text, isThinking) {
const span = document.createElement('span');
span.className = isThinking ? 'thinking' : 'content';
span.textContent = text;
streamEl.appendChild(span);
streamEl.scrollTop = streamEl.scrollHeight;
}
function streamEnd() {
const br = document.createElement('br');
streamEl.appendChild(br);
}
function extractJson(raw) {
const withoutThink = raw.replace(/<think\b[\s\S]*?<\/think>/gi, '');
const matches = [...withoutThink.matchAll(/\{[\s\S]*?\}/g)];
if (matches.length === 0) {
const fallback = [...raw.matchAll(/\{[\s\S]*?\}/g)];
if (fallback.length === 0) throw new Error('JSON не найден');
return JSON.parse(fallback[fallback.length - 1][0]);
}
return JSON.parse(matches[matches.length - 1][0]);
}
function validateResponse(obj) {
const issues = [];
if (typeof obj !== 'object' || obj === null) issues.push('не объект');
if (!('answer' in obj)) issues.push('нет поля answer');
if (!('accuracy' in obj)) issues.push('нет поля accuracy');
const a = typeof obj.answer === 'string' ? obj.answer.trim().toLowerCase() : '';
if (a !== 'доехать' && a !== 'прогуляться') issues.push(`answer="${obj.answer}" (ожидалось "доехать"/"прогуляться")`);
const acc = Number(obj.accuracy);
if (isNaN(acc) || acc < 0 || acc > 1) issues.push(`accuracy=${obj.accuracy} (вне [0..1])`);
return { ok: issues.length === 0, issues, answer: a, accuracy: isNaN(acc) ? null : acc };
}
function getSamplingParams() {
const params = {};
const temperature = parseFloat(document.getElementById('temperature').value);
const top_p = parseFloat(document.getElementById('top_p').value);
const top_k = parseInt(document.getElementById('top_k').value);
const min_p = parseFloat(document.getElementById('min_p').value);
const presence_penalty = parseFloat(document.getElementById('presence_penalty').value);
const frequency_penalty = parseFloat(document.getElementById('frequency_penalty').value);
if (!isNaN(temperature)) params.temperature = temperature;
if (!isNaN(top_p) && top_p >= 0 && top_p <= 1) params.top_p = top_p;
if (!isNaN(top_k) && top_k >= 0) params.top_k = top_k;
if (!isNaN(min_p) && min_p >= 0 && min_p <= 1) params.min_p = min_p;
if (!isNaN(presence_penalty)) params.presence_penalty = presence_penalty;
if (!isNaN(frequency_penalty)) params.frequency_penalty = frequency_penalty;
return params;
}
function updateTable() {
document.getElementById('s1_total').textContent = stats1.total || '—';
document.getElementById('s1_valid').textContent = stats1.valid || '—';
document.getElementById('s1_drive').textContent = stats1.drive || '—';
document.getElementById('s1_walk').textContent = stats1.walk || '—';
document.getElementById('s1_correct_pct').textContent = stats1.total ? ((stats1.drive / stats1.total * 100).toFixed(1) + '%') : '—';
document.getElementById('s1_err').textContent = stats1.err || '—';
document.getElementById('s2_total').textContent = stats2.total || '—';
document.getElementById('s2_valid').textContent = stats2.valid || '—';
document.getElementById('s2_drive').textContent = stats2.drive || '—';
document.getElementById('s2_walk').textContent = stats2.walk || '—';
document.getElementById('s2_correct_pct').textContent = stats2.total ? ((stats2.drive / stats2.total * 100).toFixed(1) + '%') : '—';
document.getElementById('s2_err').textContent = stats2.err || '—';
}
function resetStats(num) {
if (num === 1) {
stats1 = { total: 0, valid: 0, drive: 0, walk: 0, accSum: 0, accCount: 0, latSum: 0, err: 0 };
} else {
stats2 = { total: 0, valid: 0, drive: 0, walk: 0, accSum: 0, accCount: 0, latSum: 0, err: 0 };
}
updateTable();
}
async function streamCompletion(url, prompt, reqIndex, signal) {
const t0 = performance.now();
const samplingParams = getSamplingParams();
const body = {
model: "local-model",
messages: [{ role: "user", content: prompt }],
stream: true,
...samplingParams
};
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal
});
if (!res.ok) throw new Error('HTTP ' + res.status + ': ' + (await res.text()).slice(0, 300));
const reader = res.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let fullText = '';
let inThink = false;
streamStart(reqIndex);
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let idx;
while ((idx = buffer.indexOf('\n\n')) !== -1) {
const event = buffer.slice(0, idx);
buffer = buffer.slice(idx + 2);
const lines = event.split('\n');
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const payload = line.slice(5).trim();
if (payload === '[DONE]') continue;
if (!payload) continue;
try {
const j = JSON.parse(payload);
const delta = j.choices?.[0]?.delta?.content ?? '';
if (delta) {
fullText += delta;
const chunkForCheck = fullText.slice(-Math.max(delta.length + 10, 20));
if (!inThink && /<think\b/i.test(chunkForCheck)) inThink = true;
if (inThink && /<\/think\s*>/i.test(chunkForCheck)) {
streamChunk(delta, true);
inThink = false;
continue;
}
streamChunk(delta, inThink);
}
} catch (e) { /* skip */ }
}
}
}
streamEnd();
return { content: fullText, latency: performance.now() - t0 };
}
async function runSeries(seriesNum, seriesName) {
if (isRunning) return;
const url = document.getElementById('url').value.trim();
const count = parseInt(document.getElementById('count').value, 10);
const prompt = document.getElementById('prompt').value;
const samplingParams = getSamplingParams();
resetStats(seriesNum);
const stats = seriesNum === 1 ? stats1 : stats2;
isRunning = true;
currentController = new AbortController();
const signal = currentController.signal;
btn1.disabled = true;
btn2.disabled = true;
btnReset.disabled = true;
btnStop.style.display = 'inline-block';
addLog(`=== Запуск прогона "${seriesName}" (${count} запросов) ===`, 'ok');
addLog(`Параметры: ${JSON.stringify(samplingParams)}`, 'ok');
setStatus(`Прогон "${seriesName}": 0 / ${count}`);
try {
for (let i = 1; i <= count; i++) {
if (signal.aborted) break;
stats.total++;
try {
const { content, latency } = await streamCompletion(url, prompt, i, signal);
stats.latSum += latency;
let obj;
try {
obj = extractJson(content);
} catch (e) {
stats.err++;
addLog(`#${i} ❌ JSON parse: ${e.message} | raw tail: …${content.slice(-120)}`, 'fail');
updateTable();
continue;
}
const v = validateResponse(obj);
if (v.ok) {
stats.valid++;
if (v.answer === 'доехать') stats.drive++;
else if (v.answer === 'прогуляться') stats.walk++;
if (v.accuracy !== null) { stats.accSum += v.accuracy; stats.accCount++; }
addLog(`#${i} ✅ answer="${v.answer}" accuracy=${v.accuracy} latency=${latency.toFixed(0)}ms`, 'ok');
} else {
stats.err++;
addLog(`#${i} ⚠ schema: ${v.issues.join(', ')} | raw: ${content.slice(-140)}`, 'warn');
}
} catch (e) {
if (e.name === 'AbortError') {
addLog(`⏹ Прогон остановлен пользователем`, 'warn');
break;
}
stats.err++;
addLog(`#${i} 🚫 request: ${e.message}`, 'fail');
}
updateTable();
setStatus(`Прогон "${seriesName}": ${i} / ${count}`);
setProgress(i / count * 100);
}
addLog(`=== Прогон "${seriesName}" завершён: валидных ${stats.valid}/${stats.total} ===`, 'ok');
} catch (e) {
addLog(`🚫 Критическая ошибка: ${e.message}`, 'fail');
} finally {
isRunning = false;
currentController = null;
btn1.disabled = false;
btn2.disabled = false;
btnReset.disabled = false;
btnStop.style.display = 'none';
if (!signal.aborted) {
setStatus('');
}
}
}
btn1.onclick = () => runSeries(1, 'Без MTP');
btn2.onclick = () => runSeries(2, 'С MTP');
btnStop.onclick = () => {
if (currentController) {
currentController.abort();
setStatus('Остановка...');
}
};
btnReset.onclick = () => {
logEl.innerHTML = '';
setProgress(0);
setStatus('');
resetStats(1);
resetStats(2);
};
btnClearStream.onclick = () => {
streamEl.innerHTML = '';
};
</script>
</body>
</html>
У меня Q5. Ответ всегда один. Уверенность прыгает от 0.95 до 1.0
Скрытый текст
Вопрос: “Моя машина грязная и я хочу её помыть.Автомойка в 100 м от дома. Как мне поступить: доехать на машине или прогуляться пешком? Ответь одним словом: ‘доехать’
или ‘прогуляться’.” Дай ответ на этот вопрос и оценку точности ответа, где 1.0 - абсолютно точно, 0.0 - абсолютно не точно и это критерий оценки твоей уверенности в
этом ответе. Значения могу лежать в диапазоне от 0.0 до 1.0. Выдача должна быть в JSON со следующими полями: answer, accuracy
The user is asking a simple question: their car is dirty and they want to wash it. The car wash is 100m from home. Should they drive or walk? They want a one-word
answer: “доехать” (drive) or “прогуляться” (walk).
This is a practical/logical question. If the car is dirty and needs washing, and the car wash is only 100m away, the most practical answer depends on context:
If they drive: they drive the dirty car 100m to the car wash, then leave it there to be washed.
If they walk: they walk 100m to the car wash, but then the dirty car is still at home. They’d need to drive it there later anyway.
The key point: to wash the car, the car needs to be at the car wash. So they need to drive it there. Walking doesn’t make sense if the goal is to get the car washed -
they’d just be walking to the car wash and then still need to deal with the car at home.
So the answer is “доехать”. Now I’m thinking about how confident I am in this reasoning. It seems pretty solid - if the objective is washing the car, the car physically needs to be at the car wash, which requires driving it there. Walking wouldn’t accomplish that goal. I’d estimate this at around 0.9 confidence.
{
“answer”: “доехать”,
“accuracy”: 0.95
}
Took 0.0s
⚡ 734.4 tok/s prefill · 59.1 tok/s gen
The user asked a simple logical question and I’ve provided the answer. Let me output the JSON result.
{
"answer": "доехать",
"accuracy": 0.95
}
Обоснование: Чтобы помыть машину, её нужно доставить на автомойку. Если просто прогуляться пешком, машина останется дома грязной. Единственный логичный способ — доехать на ней до мойки. Уверенность 0.95, так как теоретически можно загнать машину на мойку на эвакуаторе или толкать (нелепо), но в реалистичных условиях «доехать» —
единственный осмысленный вариант.
⚡ 166.8 tok/s prefill · 53.3 tok/s gen
Изначально промпт плох, в итоге и ответ будет, так себе.
В идеале, что то наподобие и показывать пример, будет не лишним. Так как их супер-способность лучше всего работает именно на примерах.
Проанализируй ситуацию и выбери наиболее логичный вариант действия.
Ситуация:
Машина грязная, нужно её помыть. Ближайшая автомойка находится в 100 метрах от дома. Задача: Определи, что целесообразнее: доехать до автомойки на машине или дойти пешком.
Требования к ответу:
В поле “answer” укажи строго одно слово: “доехать” или “прогуляться”.В поле “confidence” укажи число от 0.0 до 1.0, отражающее твою уверенность в выбранном варианте (1.0 — абсолютная уверенность, 0.0 — полная неуверенность).Верни ответ строго в формате JSON. Не добавляй пояснений, markdown-разметки, комментариев или дополнительного текста.
Пример вывода: {“answer”: “доехать”, “confidence”: 0.95}
"так ты слоника не продашь" (с)
Не получится неправильный ответ от модели получить, не интересно :)
Несколько дней назад пробовал MTP для qwen3.6-27b, вчера много тестировал (для целей кодинга). Прирост скорости есть и заметный, но, увы, точность и стабильность результата заметно хуже. Тестировал на кванте Q6_K_XL. Без MTP результат стабильно оказывался качественнее. Сегодня, смотрю, вышла llama.cpp с официальной поддержкой MTP, попробую с ней потестировать, всё же с mtp 27B работает заметно шустрее.
Подскажите, какую модель лучше всего использовать для кодинга на macbook m4 pro c 48gb ram?
Qwen3.6-27B все хвалят, да и сам пробовал, могу подтвердить. С квантами Q4-Q6. Для простых и типовых кусков кода достаточно что-то варианта из Q4:...Q4_K_XL. Если нужна точность и код более-менее объемный надо проанализирвать/сгенерить, то лучше варианты Q6. Я вчера пробовал кодировать небольшую игрушку, а также игровое поле. Тестировал Q4_K_XL, Q5_K_XL, Q6_K_XL. Игру беспроблемно написали все, в рабочем виде, но с Q6_K_XL игра получилась более навороченной по спецэффектам. До этого пробовал по работе небольшой код простого микросервиса написать. Q4 выдал простое незамороченное решение. А Q8_0 выдал что-то похожее на энтерпрайз уровень: на каждый чих проверка на ошибки, вынесение констант и настроек в отдельные файлы, всё типизированно и пр. Я, кстати, выбрал первый простой вариант.
Экспериментировал в редакторе zed (там агенты из коробки, легко настраиваются). Интересно наблюдать там за генерацией. zed похоже использует свою проверку синтаксиса для проверки результата работы LLM. Сначала думает, потом генерит код, потом пишет, что чекер редактора выдает ошибки, переделывает код,... опять ошибки... и так до тех пор, пока в редакторе не останется ни одного подсвечивания (ни ошибок, ни предупреждений)... и в итоге сохраняет результат в файлы.
Сейчас я скомпилировал сегодняшнюю версию llama.cpp (где MTP нативно внедрили уже) - результат вроде стал лучше, чем было в предварительных версиях. Но отличается от того, что было без MTP. Не могу сказать, что хуже/лучше, но результаты отличаются. Буду еще тестировать.
Спасибо (не хватает кармы), попробую.
Сейчас я запускаю Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M , которая на моей конфигурации даёт примерно 28-30 токенов в секунду занимая 15 Гб памяти. Вот я и думаю, что может можно что-то побыстрее и качественнее запустить; вроде как ресурсы позволяют.
По моим ощущениям Qwen3.6 хорошо работает в квантах Q4. Я склоняюсь к использованию либо Q4, либо сразу Q6-Q8. В общем Q4 - рабочая лошадка. На сложных и объемных заданиях Q4 выдает результат проще, но всегда лучше декомпозировать задачу на простые шаги. Т.е. я бы организовал работу так, чтобы поручать локальной LLM всегда простые задачи, тогда Q4 вполне справится, причем быстро. Еще попробуйте квант IQ4_NL. Он еще компактней.
m4 pro? сколько гпу ядер? какая скорость pp/tg? почему не mlx?
Если вопрос был мне, то полная конфигурация - m4 pro, 20 gpu. pp/tg - не понял про что идёт речь.
mlx пока не включал - начинал, но показалось что будут проблемы во взаимодействии между моделью и Claude Code агентом; не стал включать в общем. Думаете стоит? Можно ли завести с Claude Code?
Прирост от MTP минимум х1.5

unsloth\Qwen3.6-27B-UD-Q4_K_XL
Win10, llama.cpp b9210, RTX 5070ti 16Gb (PCI-E 5.0 x16) + RTX 5060ti 16Gb (PCI-E 4.0 x4), i5-13600kf, 64Gb DDR5
Пожалуйста напишите ваш конфиг для ламы
./llama-server -m h:\LMS\unsloth\Qwen3.6-27B-MTP-GGUF\Qwen3.6-27B-UD-Q4_K_XL.gguf
--alias "Qwen3.6-27B-UD-Q4_K_XL-128K" -lv 4
-c 131972 --flash-attn on
--fit on --spec-type draft-mtp --spec-draft-n-max 4 -np 2
--mlock --no-mmap
--cont-batching --reasoning off --reasoning-budget 0
--batch-size 1024 --ubatch-size 256 --cache-type-k q8_0 --cache-type-v q8_0
--cache-reuse 256 --temp 0.5 --top-p 0.9 --min-p 0.03 --top-k 60 --repeat-penalty 1.12
--metrics --host 0.0.0.0
--port 1234
./llama-server -m h:\LMS\unsloth\Qwen3.6-27B-MTP-GGUF\Qwen3.6-27B-UD-Q4_K_XL.gguf
--alias "Qwen3.6-27B-UD-Q4_K_XL-128K" -lv 4
-c 131972 --flash-attn on
--fit on --spec-type draft-mtp --spec-draft-n-max 4 -np 2
--mlock --no-mmap
--cont-batching --split-mode tensor
--tensor-split 1,1 --reasoning off --reasoning-budget 0
--batch-size 1024 --ubatch-size 256 --cache-reuse 256
--temp 0.5 --top-p 0.9 --min-p 0.03 --top-k 60 --repeat-penalty 1.12 --metrics
--host 0.0.0.0 `
--port 1234
./llama-server -m h:\LMS\unsloth\Qwen3.6-27B-MTP-GGUF\Qwen3.6-27B-UD-Q4_K_XL.gguf
--alias "Qwen3.6-27B-UD-Q4_K_XL-128K" -lv 4
-c 131972 --flash-attn on
--device Vulkan0,Vulkan1 --fit on
--spec-type draft-mtp --spec-draft-n-max 4 -np 2 --mlock
--no-mmap --cont-batching
--reasoning off --reasoning-budget 0 --cache-type-k q8_0 --cache-type-v q8_0
--batch-size 1024 --ubatch-size 256 --cache-reuse 256
--temp 0.5 --top-p 0.9 --min-p 0.03 --top-k 60 --repeat-penalty 1.12 --metrics
--host 0.0.0.0 `
--port 1234
Не работает:
redfox@homeserver:~/llama.cpp/build/bin$ ./llama-server -m “/home/redfox/llama.cpp/models/qwen3/Qwen3.5-9B-UD-Q5_K_XL.gguf?download=true” -t 22 -ngl 99 --host 0.0.0.0 --port 8888 --no-mmap --spec-type draft-mtp --spec-draft-n-max 4 ggml_cuda_init: found 1 CUDA devices (Total VRAM: 8109 MiB): Device 0: NVIDIA P104-100, compute capability 6.1, VMM: yes, VRAM: 8109 MiB error while handling argument “–spec-type”: unknown speculative decoding type without draft model
usage: –spec-type [none|ngram-cache|ngram-simple|ngram-map-k|ngram-map-k4v|ngram-mod] type of speculative decoding to use when no draft model is provided (default: none)
(env: LLAMA_ARG_SPEC_TYPE)
to show complete usage, run with -h
Добрый день, делюсь опытом.
Стенд: Ryzen 5 5600 + b550m + 32Gb RAM + RTX3060 12Gb
Среда: Windows 10 (+ wsl2) + Docker + Llama.cpp server cuda
Задача: Hermes-agent + hermes-web-ui
Скорость:
Prompt processing
2154-2592 t/s
Генерация (tg)
49-54 t/s
привожу свой выстраданный docker-compose.yaml :
services:
llama-cpp-server:
image: ghcr.io/ggml-org/llama.cpp:server-cuda
container_name: llama-server
restart: unless-stopped
pull_policy: always
ports:
- "8080:8080"
volumes:
- ./models:/models:ro
environment:
- CUDA_VISIBLE_DEVICES=0
command:
# ===== Модель и проектор =====
- "-m"
- "/models/Gemma-4-E4B-Claude-Abliterated.Q4_K_M/Gemma-4-E4B-Claude-Abliterated.Q4_K_M.gguf"
- "--mmproj"
- "/models/Gemma-4-E4B-Claude-Abliterated.Q4_K_M/gemma-4-E4B-it-mmproj-BF16.gguf"
- "--jinja"
#- "--chat-template"
#- "/models/Gemma-4-E4B-Claude-Abliterated.Q4_K_M/chat_template.jinja"
# ===== Сеть =====
- "--host"
- "0.0.0.0"
- "--port"
- "8080"
# ===== GPU и фиксация в VRAM =====
- "-ngl"
- "999"
- "--no-mmap"
# ===== Контекст и слоты =====
- "-c"
- "98304"
- "-np"
- "2"
- "--kv-unified"
- "--swa-full"
# ===== Потоки CPU =====
- "-t"
- "3"
# ===== Flash Attention =====
- "-fa"
- "on"
# ===== Батчинг =====
- "-b"
- "4096"
- "-ub"
- "1024"
# ===== KV-квантование =====
#- "-ctk"
#- "q8_0"
#- "-ctv"
#- "q8_0"
# ===== Семплирование (Gemma-оптимизированное) =====
- "--temp"
- "1.0"
- "--top-p"
- "0.95"
- "--top-k"
- "64"
# ===== Таймаут =====
- "--timeout"
- "600"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
пользуйтесь.
Strix Halo 128Gb на dense модели прирост с 6-7 tps до 12-14, это конечно интересно, но для задач все равно больше moe подходит там изначально скорость 45-47tps
В llama-server на 5090 на Qwen3.6-27B Q5 200k дает 120 токенов. Без MTP дает 60 токенов. Теперь ждем версию claude distilled.
Qwen3.6 27B MTP весит на +0.3 Гб больше, а даёт ускорение в ~2 раза. С 60 t/s до 130 t/s без потерь. Что такое MTP