На Apple Silicon один и тот же чекпойнт ведёт себя по-разному в MLX-LM и в Transformers потому что меняется геометрия батча и учёт KV cache внутри unified memory. Ниже — компактная матрица сценариев шаблон таблицы памяти исполняемые параметры и пороги мониторинга чтобы команда не спорила о цифрах с ночного ноутбука.

Оглавление: сценарии выбора · батч и память · приёмка и метрики · FAQ

Статья для инженеров которые выбирают стек локального вывода на Mac с M4 и хотят согласовать компромисс между пропускной способностью и задержкой до первого токена без магических обещаний про «просто увеличьте батч». Свяжите этот материал с матрицей llama.cpp и Ollama на M4 если вы сравниваете GGUF и серверные обёртки с горячей сменой LoRA в Ollama для адаптеров. Для слоя маршрутизации моделей и политик батча на шлюзе используйте мультимодельный роутинг и чек-лист стоимости а поля трассировки и биллинга согласуйте с наблюдаемостью OpenTelemetry GenAI. Три типичные боли до таблиц первая разные фреймворки дают разный пик резидентной памяти при одной длине контекста вторая рост батча ускоряет офлайн но режет интерактив третья ноутбук команды не повторяет термическое поведение выделенного узла поэтому приёмку лучше закрепить на железе близком к продакшену.

Сценарии выбора

MLX-LM стоит брать основным контуром когда цель — быстро и предсказуемо крутить выбранную модель на Metal с короткой цепочкой команд и минимальной связкой зависимостей. Transformers оправданы если вы переносите готовые тренировочные и бенчмарк-скрипты хотите единый API для десятков архитектур или нужны тонкие хуки внимания и кастомные стратегии квантования без немедленной перепаковки весов под MLX. На практике команды оставляют MLX для сервиса вывода а Transformers для офлайн-экспериментов пока не созрела стандартизация весов.

Критерий MLX-LM Hugging Face Transformers
Цель Максимум скорости на Apple Silicon для фиксированной модели Совместимость с исследовательскими и продакшен-паттернами экосистемы
Операционные затраты Короткий путь запуска и меньше конфигурации для типового generate Больше степеней свободы и больше обязанностей по версионированию стека
Риск Узкая поддержка экзотических архитектур без доработок Легко собрать тяжёлый стек и случайно смешать dtype и внимание

Таблица батча и памяти

Абсолютные гигабайты в статье намеренно не фиксируются потому что они зависят от точной квантовки размера модели версии фреймворка и фоновых процессов macOS. Полезен шаблон измерений на одном и том же узле где вы ступенчато меняете только длину контекста затем только батч и записываете пик резидентной памяти время до первого токена и токены в секунду отдельно для интерактива и пакетного режима. KV cache растёт с длиной контекста и числом параллельных последовательностей поэтому в одной строке журнала фиксируйте и батч и одновременные сессии иначе сравнение превращается в дискуссию об именах процессов.

Поле записи Что фиксировать Зачем команде
Контекст Ступени длины окна при неизменном батче Найти излом памяти и задержки до роста батча
Батч 1 2 4 при фиксированном контексте Отделить ускорение офлайна от страданий интерактива
Давление на unified memory Пик резидентной памяти своп признаки memory pressure Понять уперлись ли в железо или в политику ОС

Шаги приёмки и метрики мониторинга

Сначала зафиксируйте карточку модели в одну строку с именем весов схемой квантования и версией токенизатора затем поднимайте только длину контекста пока кривая памяти и задержки остаётся гладкой после плато переключитесь на ступенчатый рост батча при постоянном контексте и сравните токены в секунду с временем до первого токена для двух режимов нагрузки интерактив и офлайн. В журнал исполнения добавляйте полные команды или вызовы API без сокращений чтобы воспроизводимость не зависела от памяти инженера.

python -m mlx_lm.generate --model ./weights --prompt "короткий эталонный текст" --max-tokens 256 --temp 0.2

Для Transformers на MPS явно фиксируйте устройство dtype и использование кэша внимания в одном блоке иначе сравнение с MLX будет некорректным. Ниже пример скелета вызова где важны и карта модели на mps и флаг use_cache для измерения KV.

model.to("mps") outputs = model.generate(**inputs, max_new_tokens=256, do_sample=False, use_cache=True)
  • Метрики: пик резидентной памяти время до первого токена p95 задержки декодера доля отказов по памяти длительность memory pressure троттлинг CPU GPU по температуре.
  • Порядок урезания: ограничить параллельные сессии затем контекст затем батч фиксируя причину в строке журнала.
  • Связка с продакшеном: те же поля стоимости и задержки выносите на шлюз по рецепту из статьи про мультимодельный роутинг и проверяйте полноту спанов по GenAI-наблюдаемости.

После локальной приёмки повторите таблицу на удалённом выделенном Mac с теми же версиями пакетов добавив сетевой RTT если клиент удалён и почасовую стоимость узла чтобы сравнение с ноутбуком было честным для финансового контура.

FAQ

Можно ли считать MLX всегда быстрее Transformers на M4. Нет универсального ответа скорость зависит от пары модель-стек и от того включили ли вы эквивалентные оптимизации внимания и квантования сравнение только по таблице приёмки на вашем железе является договорным источником истины.

Стоит ли отключать KV cache для диагностики. Полезно для изоляции узких мест но метрики без кэша нельзя смешивать с целевым сервисом где кэш включён иначе SLA по задержке окажется фикцией.

Что делать если unified memory заполнена но GPU формально простаивает. Проверьте конкурирующие процессы индексации и фоновые агенты разведите офлайн-батчи и интерактив по разным очередям как описано в материале про роутинг и батч.

Публичные страницы без входа: тарифы, покупка, центр помощи, каталог блога.