Qwen 3.8-27B на M1 Ultra: от 12.1 до 20.3 ток/с измеряя всё
5 контролируемых экспериментов с Qwen 3.8-27B на 128 GB M1 Ultra: планировщик QoS закрепления, MTP спекулятивное декодирование, GGUF вызов, и MoE, который потерпел поражение из‑за накладных расходов на диспетчеризацию — запуск с Kimi K3 при высокой концентрации, с доказательством powermetrics для каждого вызова.
Developed by Robert E. Beckner III (Merlin) | rbeckner.com
Весы для Qwen3.8-27B прибыли утром, и того же дня я начал приключение с Kimi K3 в режиме высокой концентрации. Я инициировал всё одним вопросом: может ли эта свежая плотная 27B — легко самая сильная открытая модель в своём классе размеров — обслуживать моих кодировщиков на реальной скорости на Mac Studio с M1 Ultra, 20 ядер CPU, 128 GB унифицированной памяти и 800 GB/с пропускной способности памяти? Общепринято, что плотные модели декодируют медленно на Apple Silicon, потому что они ограничены пропускной способностью памяти, и я хотел выяснить, может ли столько RAM и пропускной способности нарушить этот шаблон. Песочные MLX веса 4 битов достигли 15 GB, фреймворк был актуален, ничего другого не работало на машине, и первое число было 12.1 токенов в секунду. Это казалось неверным.
Я уже некоторое время обдумывал такой эксперимент. В марте, работая с другим ИИ в моей лаборатории автономной доработки, мы отследили стену под 20 ток/с для моделей 30B на M4 Max из‑за нехватки пропускной способности от ОС‑уровневой страничной памяти — и это расследование оставило руководство: powermetrics сначала, выборка по кластерам, закрепление памяти до обвинения. Запуск в этот раз начался из чистого любопытства: я слышал об оптимизациях Qwen3.8, и хотел знать, запустится ли он быстро «из коробки» на этой машине с такой памятью. Только в середине эксперимента ограниченность пропускной способностью стала ощутима. Что последовало, было спринтом контролируемых экспериментов, которые мы разработали вместе — Kimi сформулировал гипотезы и считывал аппаратные счётчики у меня, пока я держал руки на металле. Каждый эксперимент был построен, чтобы оправдать или осудить один слой стека. Победы пришли из 2 мест, которые местная инференция недооценивает: подсказки планировщика CPU и спекулятивное декодирование. Модные альтернативы 2 — сборка GGUF, обслуживаемая llama.cpp, и модель «смешения экспертов», которая казалась непобедимой на бумаге — оба потерпели поражение при измерениях. Эта статья — полный след доказательств, который мы построили вместе, потому что метод оказался ценнее любого одного числа.
Сеть-ретранслятор и сервер были первыми подозреваемыми, и оба оказались невиновными#
Путь обслуживания имел 3 перехода: моя рабочая станция, ретранслятор на базе SSH, и сервер модели на loopback Studio. Обвинять ретранслятор было бы легко, поэтому мы измерили его сначала. Внутренние тайминги сервера говорили 12.1 ток/с декодирования; чистый внутрипроцессный генерационный бенчмарк без сервера и ретранслятора вообще дал 11.5 ток/с. Транспорт стоил примерно 25% через накладные расходы на соединение, а сама модель была медленным слоем.
Одна ошибка транспорта стоила исправления в любом случае, и это тип детали, которая стоит часов, если вы никогда её не видели: Python TCP ретранслятор, использующий буферизованный read(65536), будет блокироваться с разговорным локальным протоколом, потому что буферизованный читатель ждёт полного буфера или EOF, прежде чем вернуть. Переключение на read1(), который возвращается после одного базового чтения, заставило ретранслятор вести себя. Симптом выглядел как сетевой застой; причина была в семантике stdio.
С транспортом оправданным, мы прошли обычный слой sysctl. Увеличение лимита памяти GPU (iogpu.wired_limit_mb) ничего не изменило при 128 ГБ свободной RAM, поэтому мы вернули его. Процесс Python был нативным arm64, MLX сообщал GPU как его устройство по умолчанию, и powermetrics показывал GPU в 54-62% активной загрузки, потребляя 20-23 Вт во время декодирования. Каждый подозреваемый в этом раунде прошёл свободно — что само по себе было полезным результатом, потому что это указывало расследование на CPU.
Планировщик приостановил инференс на ядрах эффективности#
Чтение powermetrics на кластере CPU вместо на машине выявило первое реальное открытие. Во время декодирования кластеры эффективности работали на 75-93% загрузке, в то время как кластеры производительности простаивали на 1-12%. Любое выполнение над SSH наследует класс качества обслуживания, который macOS читает как работу низкого приоритета, и планировщик соблюдал это указание, удерживая критически важную нагрузку на медленных ядрах.
Исправление было 1 строкой ctypes, выполненной до загрузки весов модели:
Эта фиксация, плюс загрузка текстовой башни модели через mlx-lm вместо полной стековой визуализации, переместила сырой декод от 11.5 до 15.1 ток/с — прирост 31% от размещения планировщика и более легкий загрузчик, без изменения модели. Позже мы узнали, что диапазон фиксации имеет предел: она перемещает вызывающий поток, а рабочие потоки MLX сохраняют свой собственный класс планирования. Для этой модели основной поток нести достаточную часть работы, чтобы это mattered.
Спекулятивное декодирование обеспечило прыжок, который ни один sysctl не смог#
Самый большой отдельный выигрыш пришёл от функции, которую базовая модель уже имела. Qwen3.8 поставляется с многотоковым предсказательным заголовком — небольшим вспомогательным модулем, который черновит несколько токенов вперёд — и конвертер MLX тихо отбрасывает эти 15 тензоры во время конвертации. Пакет сообщества переустанавливает заголовок как автономный 253 MB черновик, что позволило мне снова связать его с моделью, в которой он был обучен.
Шаблон обслуживания — черновик, затем проверка: черновик предлагает несколько токенов, полная модель проверяет их в 1 проходе, и принятые токены все считаются. С --draft-model и --draft-kind mtp на сервере принятие черновика измерялось 94% на реалистичных кодировочных и чат‑подсказках, а декодирование выросло с 15.1 до 20.3 ток/с на стороне сервера — 13.4 ток/с от начала до конца через ретранслятор, по сравнению с 9.0. GPU подтвердил историю: 77% активная резиденция, всё это на полной 1296 МГц, потребляя 48 В, при этом кластеры производительности 97% заняты. Предзаполнение улучшилось в той же обновлении, с 14.7 ток/с до 18-55 в зависимости от формы подсказки.
Decode speed by configuration (tok/s, M1 Ultra, 27B-4bit)Chart data
Сообщения сообщества ставят llama.cpp со спекулятивным черновиком на 25-32 ток/с для этого класса моделей на M1 Ultra, комфортно опережая наши числа MLX, поэтому мы дали противнику справедливый шанс: официальную Q4_K_M GGUF, сопоставимую модель только MTP‑черновика и текущую сборку llama.cpp с подтверждённой активной ускорённой Metal. Линия GGUF измерила базовую 9.2 ток/с и 12.2 с черновиком — примерно 40% позади стека MLX, который он должен был свергнуть.
llama.cpp остаётся отличной инженерией; разрыв, вероятно, кроется в том, как каждый рантайм обрабатывает Metal‑ядра для этого контрольного пункта на этом чипе. Устойчивая урок — проще: число, измеренное кем‑то другим на их машине, их сборке и контрольном пункте, это гипотеза о вашем. 18 GB весов противника покинули машину в тот же день, а 50 MB бинарник llama.cpp остался для будущих повторных матчей. Я писал об этом правиле раньше как о benchmark-driven development — оно привело к SEOReport from heuristics to a product — и здесь оно спасло миграцию.
3B‑активный MoE должен был победить, а счётчик GPU объяснил потерю#
Последний претендент имел самую сильную теорию — ту же общую информацию, которую мы хотели проверить с самого начала. Плотные модели декодируют медленно на Apple Silicon, потому что каждый сгенерированный токен платит за чтение почти всех весов; модель «mixture‑of‑experts» с 35B общих параметров активирует только около 3B на токен, поэтому на машине с ограничением пропускной способности её декодирование должно работать несколько раз быстрее, чем плотный 27B — каждый токен читает около 11% весов. Мы скачали 4‑битный MoE и его соответствующий MTP‑черновик, разогрели сервер и измерили 14.7 ток/с в сыром виде: ту же скорость, что и у плотной модели, которую он должен был унизить. При спекулятивном декодировании он достиг 19 ток/с на реалистичных запросах и 26.3 на высоко предсказуемом тексте — ничья с 20.3 плотной модели, без аргумента качества, чтобы разорвать ничью.
powermetrics выявил режим в 1 образце. Во время MoE декодирования GPU оставался в 0‑3% активной занятости, в то время как кластеры эффективности падали до 60‑90%. Модель тратит свой бюджет на токен на работу со стороны CPU, а измерения уровня операций показали почему: каждая отправленная операция стоит 50‑100 микросекунд накладных расходов, и эта гибридная архитектура — GatedDeltaNet линейное внимание плюс 256 экспертов за небольшим 2048‑широким скрытым состоянием — выдаёт примерно 78 операций на слой через 40 слоёв. При размере пакета 1 GPU завершает каждый маленький ядро до того, как CPU сможет поставить следующий. Машина была ограничена отправкой, и нагрузки, ограниченные отправкой, не получают выгоды от чтения меньшего количества весов на токен.
Diagram source
graph TB
A[Медленный декод
на пакете 1] --> B{GPU занят
во время декодирования?}
B -->|да| C[Ограничено пропускной способностью
меньше байт выигрывает]
B -->|нет| D[Ограничено отправкой
меньше операций выигрывает]
C --> E[MoE помогает
меньшая квантизация помогает]
D --> F[Спекулятивное декодирование
помогает больше]
style E fill:transparent,stroke:#10B981,stroke-width:2px
style F fill:transparent,stroke:#3B82F6,stroke-width:2px
Чтобы закрыть цикл, мы доказали, что путь GPU сам по себе здоров с синтетическим нагрузочным тестом: цикл умножения матриц 8192³ достиг 10.8 TFLOPS в bfloat16 с GPU на 97‑100% занятости и 68 W. Внешние данные подтвердили локальный диагноз — независимые оценки ставят этот MoE класс около 21.7 ток/с на M2 Ultra, и OpenVINO проблема документирует ту же модель, проигравшую плотному 8B на бэкендах с более слабыми путями отправки. Тяжёлый 27B с его MTP drafter удержал слот производства, и 38,5 GB MoE весов были удалены в тот же вечер.
GPU active residency during decode (powermetrics, %)Chart data
GPU active residency (%)
dense 27B + MTP
77
MoE 35B-A3B
2
matmul hog test
100
Что оставили после контролируемых экспериментов 5#
Машина теперь обслуживает Qwen 3.8-27B на уровне 20.3 токенов/сек серверной стороны, что на 68% лучше, чем в начале дня, и более надёжный результат — это чек‑лист, который мы будем использовать в каждом будущем устройстве:
Декодирование при размере пакета 1 имеет 2 режима, ограниченный пропускной способностью и ограниченный диспетчером, и 2‑секундный powermetrics образец определяет ваш, прежде чем вы потратите загрузку на неправильное исправление.
Спекулятивное декодирование — это самый эффективный регулятор обслуживания для однопользовательского устройства. Это добавило 34% здесь и усиливается каждым другим оптимизацией, потому что пакеты проверки работают GPU, пока черновик поглощает бездействующие промежутки.
Планировщик QoS — это реальный параметр вывода на macOS. Всё, что запускается через SSH, должно намеренно закреплять свои потоки, и эффект закрепления проверяется по кластерам, а не по ощущениям.
Утверждения о пропускной способности сообщества плохо переходят через контрольные точки, сборки и чипы. Локальный бенчмарк дешевле, чем миграция.
Удаление является частью рабочего процесса. Каждый соперник, который проиграл, удалил диск в течение дня, что делает следующий эксперимент честным и машину экономичной.
Удовлетворительная часть этого приключения в том, что ответы всё время находились в аппаратных счётчиках, ожидая, когда кто‑то задаст точные вопросы — и на этот раз мы задали их вместе. Локальная модель, которую вы измерили, стоит больше, чем большая модель, которую вы предположили — тот же компаундинг‑возврат, который я получаю, рассматривая локальные AI gains as infrastructure вместо тривиальных фактов. Я инициировал всё это утром, когда веса прибыли, и Kimi K3 остался в борьбе за каждый эксперимент, каждый счётчик, и это изложение из заметок лаборатории того же вечера. Следующий эксперимент уже в очереди: скомпилированные графы декодирования обещают сократить стоимость диспетчеризации на уровне операции, которая определила вердикт MoE, и когда выйдет релиз MLX, однажды, повторная встреча займет один послеобеденный час и 1 загрузку.