TL;DR (3 строки)
- Производительность MLX быстрее, но только в бенчмарках на одиночном Apple Silicon (быстрее на 3–12%)
- Агент в продакшне llama.cpp + Ollama лучше подходит для агентов и продакшн-инференса (HTTP runtime — решающая переменная)
- Настоящий потолок В большинстве сценариев узким местом является пропускная способность памяти, а не фреймворк — при свопировании оба одинаково деградируют
Ключевой вывод статьи
Близость к железу: MLX ближе (в 3× меньше dispatch-вызовов, более короткий путь квантования). Близость к инженерии: llama.cpp / Ollama ближе (готовый HTTP runtime, Claude Code подключается без настройки). Эти два измерения нельзя смешивать.
Единое объяснение на уровне первых принципов
Принципиальное различие MLX и llama.cpp — не в том, кто быстрее на несколько процентов, а в двух принципиально разных философиях выполнения:
- graph-fusion execution (MLX): lazy-сборка графа операторов → слияние во время выполнения → батчевая диспетчеризация
- per-op dispatch execution (llama.cpp): каждый оператор отправляется независимо → кросс-платформенная совместимость → предсказуемые накладные расходы
А также две совершенно разные инженерные ориентации:
- hardware-native specialization (MLX): только Apple — максимальная отдача от одного железа
- engineered portability (llama.cpp): CUDA / Metal / Vulkan / CPU — переносимость в приоритете
Все различия в производительности сводятся к трём вещам:
① количество dispatch-вызовов ② возможность kernel fusion ③ является ли пропускная способность памяти узким местом
Границы системы: область применения и ограничения выводов статьи
Вывод с явными границами достовернее расплывчатого описания. Все выводы этой статьи о производительности и выборе стека MLX vs llama.cpp справедливы при следующих условиях:
✅ Применимые сценарии
- Apple Silicon (серии M1–M4 / M5)
- batch = 1 / малый batch инференс (фаза decode)
- decoder-only transformer (основные модели 7B–14B)
- Metal backend (нативный GPU-путь macOS)
- Локальный инференс на одной машине / небольшой командный узел
- Агентские инструменты: Claude Code / Cursor / Open WebUI
❌ Неприменимые сценарии
- CUDA / NVIDIA multi-GPU serving
- Large batch training (пакетное обучение)
- Distributed inference (распределённый инференс на нескольких узлах)
- Speculative decoding pipeline
- Модели MoE (существенно иная архитектура)
- Облачные GPU-инстансы (без Apple Silicon)
Выводы статьи неприменимы к сценариям с NVIDIA / AMD GPU — это область CUDA vs ROCm, не связанная с архитектурой Unified Memory Apple Silicon.
Определение ключевых терминов: Metal dispatch · Kernel fusion · Unified memory · GGUF vs MLX format
Прежде чем погружаться в технические детали, выровняем понимание нескольких базовых концепций. Именно они чаще всего путаются в контексте LLM-инференса на Apple Silicon.
- Metal dispatch (Metal compute dispatch)
- Минимальная единица отправки compute kernel с CPU на GPU Apple Silicon. Каждый dispatch требует синхронного подтверждения между CPU и GPU, создавая накладные расходы порядка 1–3 мкс. Чем больше dispatch-вызовов — тем выше суммарные затраты CPU на ожидание GPU.
- Kernel fusion (слияние ядер)
- Объединение нескольких независимых операторов (например, matmul → bias add → activation) в один GPU kernel для сокращения числа dispatch-вызовов и обращений к глобальной памяти для промежуточных результатов. MLX делает это автоматически через lazy evaluation; llama.cpp полагается на вручную написанные слитые ядра (например, flash attention) с ограниченным охватом.
- Unified Memory (унифицированная память, Apple Silicon)
- CPU и GPU Apple Silicon совместно используют один физический банк памяти — отдельной видеопамяти не существует. Веса модели после загрузки не нужно копировать между CPU и GPU — в этом состоит аппаратное преимущество инференса на Mac. Metal backend MLX и llama.cpp автоматически используют Unified Memory, но аллокатор памяти MLX ближе к этой модели.
- GGUF (формат квантования llama.cpp)
- Формат квантования моделей, используемый llama.cpp; поддерживает несколько уровней точности от Q2_K до Q8_0. Файлы моделей имеют расширение
.ggufи доступны для скачивания напрямую с Hugging Face. Ollama использует GGUF для управления моделями; формат несовместим с MLX. - MLX format (формат квантования mlx-community)
- Формат моделей для MLX на основе safetensors + метаданных квантования MLX. Конвертируется через
mlx_lm.convertили загружается готовым из mlx-community. Ядра квантования напрямую привязаны к Metal shaders и полностью несовместимы с GGUF; одну базовую модель нужно конвертировать отдельно для каждого фреймворка.
Обзор архитектуры MLX vs llama.cpp: полное сравнение фреймворков инференса Apple Silicon
Прежде чем вдаваться в детали, построим общее понимание с помощью одной таблицы. Принципиальное расхождение mlx vs llama.cpp — в проектных целях: один оптимизирован исключительно под Apple, другой — под кросс-платформенную переносимость.
| Параметр | MLX | llama.cpp |
|---|---|---|
| Проектная цель | Только Apple Silicon | Кросс-платформенность (CUDA / Metal / Vulkan / CPU) |
| Модель диспетчеризации | Lazy graph fusion (анализ графа → батчевый dispatch) | Per-op dispatch (каждый оператор независимо) |
| Metal-подход | Runtime JIT kernel (специализируется под форму тензора) | Precompiled kernels (упакованы в бинарник) |
| Модель памяти | Native unified memory (нативный аллокатор Unified Memory) | ggml allocator (универсальный, не специфичен для Apple) |
| Формат квантования | safetensors + MLX metadata (mlx-community) | GGUF (Q2_K – Q8_0) |
| HTTP runtime | Нет встроенного (нужен собственный FastAPI-шлюз) | Ollama из коробки (:11434) |
| Прямое подключение к Claude Code / Cursor | ❌ Нужен собственный шлюз | ✅ Ollama без настройки |
| Подходящие сценарии | бенчмарки, исследования, LoRA-дообучение | Agent runtime, продакшн-инференс, командный шаринг |
Последние две строки определяют выбор стека; разница tok/s рассматривается в § Реальные источники разницы в производительности и не влияет на решение об Agent runtime.
Apple Silicon LLM Inference Stack Model (2026)
Переведём таблицу выше в четырёхуровневую модель разделения ответственности — это наиболее простой фреймворк для понимания всех выводов статьи:
┌─────────────────────────────────────────────────┐ │ Application Layer │ │ Claude Code · Cursor · Open WebUI │ ├─────────────────────────────────────────────────┤ │ Runtime Layer │ │ Ollama (llama.cpp) │ FastAPI (MLX wrapper) │ │ ✅ Рекомендован для агентов │ ⚠️ Самостоятельная сборка │ ├─────────────────────────────────────────────────┤ │ Compute Layer │ │ ggml-metal (per-op) │ MLX (graph fusion) │ │ Кросс-платформ., предкомп. ядра │ Apple-only, JIT │ ├─────────────────────────────────────────────────┤ │ Hardware Layer │ │ Apple Silicon Unified Memory │ │ CPU + GPU разделяют физическую память · zero-copy · высокая полоса │ └─────────────────────────────────────────────────┘
Ключевой вывод: выбор стека происходит только на уровне Runtime. Разница в производительности на уровне Compute (MLX vs ggml-metal) не влияет на выбор на уровне Runtime — они обслуживают разные уровни и не заменяют друг друга.
Ключевое различие: количество Metal dispatch (480 vs 160) — источник производительности инференса на Apple Silicon
Это самое важное число во всей статье. Понять разницу в количестве dispatch-вызовов — значит понять первопричину разницы в производительности MLX vs llama.cpp.
Для трансформера 7B генерация каждого токена требует одного прохода через 32 слоя. Каждый слой включает операторы QKV projection, attention score, softmax, output projection, FFN gate, FFN up/down, RMS Norm, RoPE и другие:
llama.cpp (per-op dispatch): 32 слоя × ~15 операторов/слой ≈ 480 Metal dispatch-вызовов
MLX (после lazy graph fusion): 32 слоя × ~4–5 fusion block/слой ≈ 128–160 Metal dispatch-вызовов
💡 Ключевое наблюдение (для цитирования)
Преимущество MLX не в более мощных вычислениях, а в сокращении числа dispatch-вызовов CPU↔GPU примерно в 3 раза.
При batch=1 накладные расходы каждого [commandBuffer commit] составляют ~1–3 мкс. 480 вызовов vs 160 вызовов — экономия ~0,3–1 мс на каждый токен. Именно это и есть главный механизм лидерства MLX в бенчмарках, а не более совершенные алгоритмы ядер.
Каждый Metal dispatch — это точка синхронизации CPU-GPU: CPU должен дождаться подтверждения от GPU о постановке command buffer в очередь, прежде чем инициировать следующий вызов ядра. Когда время GPU compute мало (инференс с малым batch), накладные расходы dispatch становятся узким местом. Lazy evaluation MLX объединяет несколько операторов в один dispatch, полностью устраняя эту стоимость синхронизации.
Модель задержки токенов (Dispatch Cost Model)
Разобьём задержку генерации одного токена на три измеримых компонента:
Задержка токена ≈ dispatch_count × cpu_sync_cost ← накладные расходы dispatch (источник различий фреймворков) + gpu_compute_time ← реальное время GPU (пропускная способность / bound по вычислениям) + memory_bandwidth_time ← время переноса весов (физический потолок)
Типичное распределение при batch=1, модель 7B, M4 Mac Mini 16GB:
| Компонент | llama.cpp | MLX | Примечание |
|---|---|---|---|
dispatch_count × cpu_sync_cost |
~480 × 2 мкс ≈ 0,96 мс | ~160 × 2 мкс ≈ 0,32 мс | Источник различий фреймворков, ~10–30% от общей задержки |
gpu_compute_time |
~1–3 мс (у обоих примерно одинаково) | Определяется вычислительной мощностью и форматом квантования | |
memory_bandwidth_time |
~4 ГБ ÷ 120 ГБ/с ≈ 33 мс (доминирующий компонент) | Физический потолок, фреймворк не может его оптимизировать | |
Приведённые цифры — приблизительные оценки (batch=1, 7B Q4_K_M, M4 16GB). memory_bandwidth_time является доминирующим компонентом, что объясняет, почему преимущество MLX стремится к нулю при насыщении пропускной способности.
| Параметр | llama.cpp (ggml-metal) | MLX |
|---|---|---|
| Жизненный цикл ядра | Предкомпилированные, упакованы в бинарник (.metal → .metallib) |
Компиляция по требованию во время выполнения, первый запуск медленнее, затем кэшируется |
| Слияние ядер | Ограниченное: некоторые вручную написанные слитые ядра (например, flash attention) | Автоматическое слияние на уровне фреймворка; lazy graph объединяет соседние операторы |
| Число dispatch на forward pass (7B) | ~400–600 (пооператорно) | ~80–160 (после слияния) |
| Путь CPU-диспетчеризации | ggml scheduler → Metal command buffer | MLX scheduler напрямую пишет в Metal command buffer |
| Мультипоточный параллелизм | Одна Metal command queue (основная конфигурация) | Поддержка нескольких stream, возможен параллелизм соседних слоёв |
Почему появился MLX: отправная точка проектирования фреймворка инференса для Apple Silicon
В конце 2023 года команда машинного обучения Apple открыла исходный код MLX. Его мотивация необычна — не кросс-платформенность, а максимальное использование одного железа.
До появления MLX LLM-инференс на Apple Silicon опирался на:
- Core ML / MPS: официальный стек инференса Apple, закрытый формат моделей, неудобен для исследователей
- llama.cpp + Metal: основной выбор сообщества, но ggml проектировался для мира CUDA и привносит абстракционные накладные расходы кросс-платформенности
- PyTorch + MPS backend: неполное покрытие операторов MPS, скорость инференса не полностью использует возможности Apple Silicon
Вывод инженеров Apple: чтобы по-настоящему задействовать комбинацию Unified Memory + высокоскоростная шина + Metal GPU, нужен фреймворк для работы с массивами, спроектированный с нуля под Apple Silicon.
Четыре ключевых принципа проектирования MLX
- CPU и GPU делят одно адресное пространство памяти — передача данных без копирования (zero-copy)
- Lazy evaluation: вычисления не выполняются немедленно, операции откладываются до момента, когда результат действительно нужен, и пакетно оптимизируются
- Динамический граф + JIT, API близок к NumPy / JAX — удобен для исследователей
- Metal compute shaders компилируются по требованию во время выполнения, не упакованы заранее
Архитектура llama.cpp: мультиплатформенная абстракция ggml + Metal backend + квантование GGUF
llama.cpp был опубликован Georgi Gerganov в марте 2023 года с целью запустить LLaMA на потребительском железе с помощью чистого C/C++; переносимость — приоритет номер один.
ggml: универсальный тензорный бэкенд
Ядро llama.cpp — библиотека ggml, лёгкая C-библиотека тензоров с поддержкой множества типов железа через плагины бэкендов:
| Бэкенд | Железо | Условие активации |
|---|---|---|
GGML_METAL | GPU Apple Silicon | macOS, -DLLAMA_METAL=ON |
GGML_CUDA | GPU NVIDIA | Linux/Windows + CUDA |
GGML_VULKAN | GPU AMD / Intel | Vulkan driver |
GGML_CPU | Все CPU | Fallback по умолчанию |
Преимущество — один код на всех платформах; цена — каждый бэкенд является универсальным адаптером без глубокой оптимизации под конкретное железо, и абстракция добавляет накладные расходы диспетчеризации.
Metal backend (ggml-metal) и предкомпилированные ядра
На Apple Silicon llama.cpp использует ggml-metal.metal — предкомпилированный файл Metal shaders, содержащий:
- Матричное умножение: специализированные ядра для каждого формата GGUF (Q4_K, Q8_0 и др.)
- Softmax, RoPE, RMS Norm: отдельные ядра для каждого оператора
- Операции KV cache: переиспользование представлений памяти без копирования
Ключевое ограничение: ядра предкомпилированы и фиксированы — во время выполнения они не переоптимизируются под форму тензора. При изменении длины последовательности или batch size параметры ядра передаются через Metal buffer, но само ядро остаётся неизменным — именно здесь MLX с runtime-специализацией получает преимущество.
Формат квантования GGUF
llama.cpp использует GGUF с точностью квантования от Q2_K (максимальное сжатие) до Q8_0 (близко к fp16). Для каждого формата в Metal-ядрах есть соответствующая реализация — именно поэтому производительность на Apple Silicon достойная. Однако это означает, что каждый новый формат квантования требует поддержания отдельного набора ядер.
Архитектура MLX: lazy evaluation + runtime JIT kernel + Unified Memory Apple Silicon
Принципиальное отличие MLX от llama.cpp — в положении абстракционного слоя: Metal в llama.cpp — это «подключаемый бэкенд», а весь граф вычислений MLX с самого начала живёт в семантике Metal.
Lazy Evaluation + слияние вычислительного графа
- Когда Python-код вызывает операторы MLX, они не выполняются немедленно — только записываются узлы вычислительного графа
- При вызове
mx.eval()фреймворк анализирует весь граф - Соседние объединяемые операторы (matmul + bias + activation и т. д.) сливаются в один Metal dispatch
- Сгенерированный Metal compute shader компилируется во время выполнения, кэшируется и переиспользуется
Глубокое использование Unified Memory
- Все тензоры выделяются в едином пуле памяти; CPU и GPU разделяют один физический адрес
- Нет понятия «видеопамять» — после загрузки GPU читает веса напрямую, без аналога
cudaMemcpy - Metal buffer и MLX-массив ссылаются на один и тот же указатель в нижнем слое — передача аргументов без копирования (zero-copy)
Metal backend llama.cpp также использует Unified Memory (это аппаратная особенность Apple Silicon, доступная любой Metal-программе), но аллокатор памяти ggml не проектировался специально для этого — есть дополнительное выравнивание и логика аллокации.
GGUF vs MLX: две несовместимые экосистемы квантования
MLX использует safetensors + метаданные квантования MLX, которые полностью несовместимы с GGUF. Одну базовую модель нужно конвертировать отдельно:
- Для llama.cpp: скачать с HuggingFace → конвертировать через
convert.pyв GGUF с квантованием - Для MLX: конвертировать через
mlx_lm.convertили скачать предконвертированную версию из mlx-community
Поддерживается квантование 4-bit, 8-bit и смешанная точность; реализации операторов напрямую привязаны к Metal shaders — именно поэтому путь deквантования в MLX короче, чем в GGUF.
Реальные источники разницы в производительности MLX vs llama.cpp: dispatch vs пропускная способность памяти
Это самая часто неправильно интерпретируемая часть mlx benchmark. Преимущество MLX в скорости обусловлено не лучшим алгоритмом матричного умножения, а тремя измеримыми инженерными факторами:
1. Слияние dispatch: в 3 раза меньше синхронизаций CPU-GPU
Как описано в § Ключевое различие, MLX сокращает число Metal dispatch с ~480 до ~160 на токен. Наибольший выигрыш даётся при batch=1 и коротких последовательностях — когда время GPU compute мало, накладные расходы CPU на диспетчеризацию велики.
2. Runtime-специализация ядер: оптимизация под конкретные формы тензоров
MLX компилирует Metal shaders при первом запуске с конкретной формой, что позволяет настраивать tile size и размер workgroup под конкретные размерности матриц. Предкомпилированные ядра llama.cpp используют консервативные универсальные параметры и плохо адаптируются к нестандартным длинам последовательностей.
3. Более короткий путь деквантования
Для поддержки нескольких платформ деквантование (dequant) в llama.cpp должно учитывать CPU fallback путь; ядра квантования MLX используют Metal как единственную цель — деквантование + матричное умножение могут выполняться в одном shader, сокращая количество обращений к глобальной памяти.
Справочный диапазон измеренных значений
Данные из Macstripe Lab, Llama-3.1-8B / Q4_K_M, batch=1, измерение 2026-06. Только для оценки тенденций, не является основанием для покупки или выбора стека.
| Устройство | llama.cpp (Ollama) tok/s | MLX tok/s | Разница |
|---|---|---|---|
| M4 Mac Mini 16GB | 27–31 | 28–33 | +3%–7% |
| M4 Mac Mini 24GB | 34–39 | 37–43 | +5%–10% |
| M4 Pro 48GB | 72–80 | 80–90 | +8%–12% |
Закономерность: чем больше памяти и чем крупнее модель, тем заметнее преимущество MLX. При большой модели время GPU compute на forward pass длиннее, а абсолютный выигрыш от слияния dispatch больше; на 16 ГБ с моделью 7B пропускная способность уже является узким местом, что сглаживает выигрыш.
Модель сходимости производительности (Performance Convergence Model)
Визуализация тренда из таблицы: как преимущество MLX растёт с увеличением объёма памяти и обнуляется в точке насыщения пропускной способности:
Преимущество MLX (delta tok/s) │ +12% │ ● 48GB (14B) │ · +8% │ · │ · +5% │ ● 24GB (7B–14B) │ · +3% │ ● 16GB (7B) │ 0% │━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ← потолок пропускной способности памяти │ (своп) (своп) -∞% │ ↓ при свопе оба рушатся │ └──────────────────────────────────────────── 16GB 24GB 48GB объём памяти
Ключевой смысл графика: объём памяти определяет, на каком участке кривой вы находитесь. Выигрыш от перехода 16 ГБ → 24 ГБ (недоступный при смене фреймворка) намного превышает разницу MLX vs llama.cpp в 3–12%.
Почему в большинстве сценариев разница mlx benchmark ≈ 0: пропускная способность памяти — настоящий потолок
Не во всех сценариях преимущество MLX видно. Это самая важная «контринтуитивная» ценность статьи:
Насыщение пропускной способности памяти: различия фреймворков нивелируются физическим ограничением
Потолок производительности инференса batch=1 — это пропускная способность переноса весов, а не вычислительная мощность GPU. Генерация каждого токена требует чтения весов всех слоёв (7B Q4 ≈ 4 ГБ перемещения данных). Пропускная способность памяти M4 составляет ~120 ГБ/с (версия 16 ГБ) — этот физический предел определяет верхнюю границу tok/s. Ни MLX, ни llama.cpp не могут его преодолеть — при насыщении пропускной способности они сходятся.
При свопировании оба одинаково деградируют
При нехватке Unified Memory система выгружает веса на SSD, пропускная способность падает со 120 ГБ/с до 3–5 ГБ/с (предел последовательного чтения NVMe). Tok/s у обоих опускается до единиц, различия фреймворков полностью исчезают. Единственное решение — увеличить объём памяти; смена фреймворка не помогает.
Длинный контекст prefill: GPU становится узким местом, доля накладных расходов dispatch стремится к нулю
Prefill (обработка длинного prompt) — вычислительно интенсивная операция, GPU загружен почти на 100%. Разница во времени prefill при 2048-токенном prompt обычно <5%.
Малые модели (≤3B): слишком короткое абсолютное время вычислений
Веса модели 3B Q4 составляют ~2 ГБ, время GPU на один forward pass ~5–10 мс. Доля экономии 0,3–1 мс от слияния dispatch растёт, но абсолютное значение слишком мало для ощутимой разницы.
Высокоточное квантование (Q8_0 / fp16): пропускная способность насыщается раньше
Чем выше точность, тем больше веса, тем раньше пропускная способность становится узким местом. Модель Q8 на машине с 16 ГБ почти гарантированно уйдёт в своп — в этом случае говорить о различиях фреймворков бессмысленно.
Выбор стека Ollama vs MLX: почему Agent runtime не зависит от tok/s
Если MLX ближе к железу на аппаратном уровне, почему рекомендуемый стек для Claude Code — Ollama (llama.cpp)? Потому что это вопросы из разных измерений.
HTTP-сервер — ключевая переменная
Ollama предоставляет полноценную инфраструктуру сервиса инференса поверх llama.cpp:
- Готовый HTTP-сервис инференса (
:11434, OpenAI-совместимый API) — Claude Code подключается напрямую, без настройки - Управление жизненным циклом моделей (
ollama pull / rm, автоматическая выгрузка неиспользуемых моделей) - Параллельная обработка запросов (несколько окон Claude Code разделяют один экземпляр модели без повторной загрузки)
- Командный шаринг по локальной сети (
ollama serve --host 0.0.0.0, вся команда подключается к одному:11434)
У MLX ничего из этого нет. Подключение MLX к Claude Code потребует: построить FastAPI-шлюз → реализовать логику загрузки/выгрузки моделей → обработать очередь параллельных запросов → поддерживать слой совместимости с OpenAI API. Это полный стек сервиса инференса, а не одна команда.
Доля разницы tok/s в цикле вызовов инструментов агента <5%
В цикле вызовов инструментов агента каждый раунд Claude Code включает: сборку prompt → HTTP-запрос → ожидание ответа → разбор вызова инструмента → выполнение инструмента → следующий раунд. Распределение задержки:
| Фактор | Доля (типичная агентская задача) | Оптимизируется фреймворком? |
|---|---|---|
| Время выполнения инструментов (чтение/запись файлов, shell-команды) | 50%–70% | Нет |
| HTTP round-trip + TTFT модели | 20%–35% | В основном зависит от объёма памяти |
| Разница tok/s на уровне фреймворка (MLX vs llama.cpp) | <5% | Да, но влияние минимально |
Определяющие переменные для качества работы агента — объём памяти и размер модели. Различия фреймворков полностью поглощаются HTTP-слоем и временем выполнения инструментов.
Инженерные границы Ollama vs MLX
Чёткое понимание границ каждого инструмента устраняет путаницу:
- Ollama (llama.cpp): HTTP runtime — решает проблему «как подключить»
- MLX: инструмент вычислительного слоя — решает проблему «как быстро запустить в Python»
- Они не исключают друг друга: на одном Mac можно запускать MLX для бенчмарков и одновременно держать Ollama для Claude Code
→ Полная логика выбора Ollama vs MLX: Ollama vs MLX: какой стек для локальных моделей Claude Code?
→ Практика развёртывания MLX на M4 Pro: Руководство по локальным LLM на Apple Silicon M4 Pro: тестирование производительности и развёртывание MLX
MLX или llama.cpp? (Руководство по выбору за 30 секунд)
Выбирайте исходя из реального сценария — нет однозначно лучшего варианта:
Модель выводов для инференса LLM на Apple Silicon: три правила для цитирования (mac m4 llm inference speed)
Сжимаем все выводы статьи в переиспользуемый, цитируемый фреймворк принятия решений:
Модель выводов (можно цитировать напрямую)
- Потолок производительности = пропускная способность памяти
Верхняя граница tok/s для инференса batch=1 на Apple Silicon определяется пропускной способностью Unified Memory (~120 ГБ/с для M4); фреймворк не может преодолеть физические ограничения. - Различие фреймворков = накладные расходы dispatch
Разница в производительности MLX vs llama.cpp (3–12%) обусловлена разным числом Metal dispatch (480 vs 160), а не более совершенными алгоритмами операторов. - Выбор системы = runtime vs research
Для Agent runtime — Ollama (llama.cpp): HTTP-сервер — решающая переменная. Для исследований / бенчмарков / дообучения — MLX: ближайший к железу вычислительный слой.
Оптимизация фреймворка даёт 5–15%, объём памяти определяет 85%.
Любая дискуссия MLX vs llama.cpp в конечном счёте сводится к этой фразе.
Связанные вопросы: распространённые поисковые запросы о выборе стека на Apple Silicon
Что лучше для локального развёртывания — MLX или llama.cpp?
Зависит от того, что понимать под «развёртыванием». Если речь о развёртывании сервиса инференса (для Claude Code / Cursor / собственного API) — llama.cpp + Ollama предпочтительнее: HTTP-сервер из коробки, хорошее управление моделями. Если нужна исследовательская среда (тестирование новых моделей, бенчмарки, Python-скрипты) — MLX подходит лучше: ближе к железу, гибкий Python-интерфейс.
Почему LLM-инференс на Apple Silicon не использует CUDA?
В Apple Silicon нет GPU от NVIDIA и нет поддержки CUDA. GPU Apple работает через Metal API; архитектура Unified Memory позволяет CPU и GPU совместно использовать один физический банк памяти — принципиально иначе, чем модель с отдельной видеопамятью NVIDIA. MLX и llama.cpp оба работают на Metal и используют Unified Memory, а не видеопамять.
Чем отличаются форматы квантования GGUF и MLX?
Форматы несовместимы и представляют собой две независимые экосистемы квантования. GGUF (llama.cpp) поддерживает несколько уровней точности от Q2_K до Q8_0 со специализированными реализациями в ядрах ggml-metal; MLX использует safetensors + метаданные квантования MLX, ядра квантования напрямую привязаны к Metal shaders. Одну базовую модель нужно конвертировать отдельно для каждого фреймворка.
Почему для LLM на Mac в основном используют Ollama?
Ollama решает инженерную задачу «как пустить LLM в дело»: одна команда запускает HTTP-сервер, OpenAI-совместимый API, Claude Code / Cursor / Open WebUI подключаются напрямую. Под капотом — llama.cpp с Metal-ускорением на Apple Silicon, производительности достаточно, Python-окружение и самостоятельный сервис не нужны.
FAQ
MLX vs llama.cpp — что быстрее? Насколько велика разница?
В большинстве бенчмарков MLX быстрее на 3–12% — главным образом за счёт слияния ядер (~480 vs ~160 dispatch) и runtime-специализации Metal kernels. Однако при batch=1 с насыщенной пропускной способностью, свопировании или длинном prefill разница стремится к нулю. Чем больше памяти (узел 48 ГБ) и чем крупнее модель (14B+), тем заметнее преимущество MLX; на 16 ГБ с моделью 7B разница составляет лишь 3–7%.
В чём принципиальное отличие Metal backend llama.cpp от MLX?
llama.cpp абстрагирует несколько платформ через ggml; Metal — один из многих бэкендов, ядра предкомпилированы, каждый оператор отправляется независимо. MLX с самого начала проектировался только для Apple Silicon: lazy evaluation объединяет операторы во время выполнения, dispatch в 3× меньше, путь квантования короче, выравнивание под Unified Memory глубже.
Можно ли конвертировать GGUF в MLX и обратно?
Прямая конвертация невозможна — форматы полностью независимы. GGUF генерируется из исходных весов через convert.py от llama.cpp; MLX-формат конвертируется через mlx_lm.convert или скачивается напрямую из mlx-community. Одну базовую модель нужно конвертировать отдельно для каждого фреймворка.
Почему Claude Code выбирает Ollama, а не MLX?
Ollama предоставляет готовый HTTP-сервис (:11434, OpenAI-совместимый) без необходимости строить шлюз. В цикле вызовов инструментов агента разница tok/s на уровне фреймворка составляет менее 5% от общей задержки — узкое место лежит во времени выполнения инструментов и HTTP-слое. MLX не имеет встроенного HTTP-сервера; подключение к Claude Code требует полного стека самостоятельно построенного сервиса инференса, что поглощает всё преимущество в скорости.
Кто лучше переносит своп?
Оба серьёзно деградируют, разница исчезает. Пропускная способность памяти падает со 120 ГБ/с до 3–5 ГБ/с (предел NVMe); SSD-узкое место не поддаётся оптимизации фреймворком. Единственное решение — увеличить объём памяти (узел 24 ГБ или 48 ГБ).
Можно ли установить MLX и Ollama одновременно?
Можно, они не конфликтуют. Рекомендуемая конфигурация: Ollama в фоне обслуживает Claude Code, MLX запускается по требованию в Python-окружении для бенчмарков или скриптов дообучения. Оба делят одну Unified Memory, но форматы моделей разные — управлять ими нужно раздельно.