Skip to content

Feature Request: Metal prefill speedup (~68%) on M5 Max (NAX / tensor API kernels) #30287

Closed as not planned

Description

@LXD-8

Prerequisites

  • I am running the latest code. Mention the version if possible as well.
  • I carefully followed the README.md.
  • I searched using keywords relevant to my issue to make sure that I am creating a new issue that is not already open (or closed).
  • I reviewed the Discussions, and have a new and useful enhancement to share.

Feature Description

Docs

Feature Description

On a MacBook Pro M5 Max (128 GB) the Metal backend prefills at roughly 60% of oMLX. The main reason is that the M5 NAX matrix units are mostly unused by the current ggml-metal kernels. I made 6 changes, all of them in the Metal backend only, each one switchable off with an environment variable. Compared with upstream b11541 (the latest tag at time of writing):

test before after delta
pp, 8k context 633 t/s 1064 t/s +68%
pp, 32k context 528 t/s 884 t/s +67%

Decode speed is unchanged.

Per-change contribution (llama-bench, 8k context, pp2048):

change contribution how measured
NAX flash attention +7.9% leave-one-out
f16 activations +10.9% single change
NAX mat-mat + split-K for long K +5.9% on top of f16 activations
GDN: 4 lanes per state row +5.4% leave-one-out
int8 mat-mat +31.8% leave-one-out
SwiGLU fused into the consuming mat-mat +1.2% / +3.7% leave-one-out, two runs

These numbers come from different measurement setups and do not add up to the total. See section 5 of the full doc.

Provenance of the ideas: the NAX flash attention, the NAX mat-mat with split-K, the GDN lane change and the int8 mat-mat follow ideas from MLX and oMLX (steel_attention_nax, steel_gemm_fused_nax, gated_delta_pipelined, oq_a8), where such kernels are always on and have no switch; the f16 activation conversion and the SwiGLU fusion are my own. The kernels here are new ggml-metal implementations.

Related work in this repo

I searched issues, PRs and Discussions for M5, NAX, tensor API, prefill, split-K, GDN, SwiGLU and int8. No existing item proposes this set of changes as a whole. Closest neighbours, and how this work relates to them:

AI usage disclosure: AI tools were used for research and for parts of the implementation and testing; I checked every result. I wrote the original text in Chinese; the English text above is a translation, which I back-translated and compared against the Chinese original below for consistency.


文档

Feature Description

在 MacBook Pro M5 Max(128 GB)上,Metal 后端的预填充速度只有 oMLX 的约 60%。主要原因是 M5 的 NAX 矩阵单元没有被现有 ggml-metal kernel 充分利用。我在 Metal 后端内做了 6 项改动,每项都可以用一个环境变量单独关闭。与上游 b11541(撰写时的最新 tag)相比:

测试 改动前 改动后 提升
预填充,8k 上下文 633 t/s 1064 t/s +68%
预填充,32k 上下文 528 t/s 884 t/s +67%

解码速度不变。

各项改动的贡献(llama-bench,8k 上下文,pp2048):

改动 贡献 测法
NAX flash attention +7.9% 留一法
f16 激活 +10.9% 单独开启
NAX 矩阵乘 + 长 K split-K +5.9% 在 f16 激活基础上
GDN 每状态行 4 个 lane +5.4% 留一法
int8 矩阵乘 +31.8% 留一法
SwiGLU 融合进下一个矩阵乘 +1.2% / +3.7% 留一法,两次测量

这些数字的测法不同,不能相加,详见完整版文档第 5 节。

思路来源:NAX flash attention、NAX 矩阵乘(含长 K 的 split-K)、GDN lane 调整、int8 矩阵乘这四项借鉴了 MLX 和 oMLX 的思路(steel_attention_nax、steel_gemm_fused_nax、gated_delta_pipelined、oq_a8);在这两个引擎里,对应的 kernel 常开、没有开关。f16 激活转换和 SwiGLU 融合这两项是我自己的。这里的 kernel 都是为 ggml-metal 新写的实现。

与社区已有工作的关系

我用 M5、NAX、tensor API、prefill、split-K、GDN、SwiGLU、int8 等关键词搜过 issues、PRs 和 Discussions,没有找到整体重复的条目。最接近的几项以及本工作与它们的关系:

AI 使用说明:调研、部分实现和测试用了 AI 工具,所有结果都经过我人工检查。正文由我用中文先写好,上面的英文版由 AI 翻译;翻译结果已回译成中文,并与我的中文原文逐条核对,事实一致。

Motivation

  • With all changes enabled, prefill roughly matches oMLX 0.7.1: +3% at 8k, -6% at 32k. Comparison setup: MTP and KV quantization disabled on both sides, oMLX int8 prefill enabled.
  • Correctness: test-backend-ops passes for MUL_MAT, FLASH_ATTN_EXT and GATED_DELTA_NET. Perplexity is 3.5172 with all changes on and with all of them off.
  • Hardware: MacBook Pro, M5 Max (40-core GPU, 128 GB), macOS 27.2, model Qwen3.8-27B Q4_K_M.

  • 全部开启后,预填充与 oMLX 0.7.1 基本持平:8k 领先 3%,32k 落后 6%。对比条件:两边都关闭 MTP 和 KV 量化,oMLX 开启其 int8 预填充。
  • 数值正确性:test-backend-ops 的 MUL_MAT、FLASH_ATTN_EXT、GATED_DELTA_NET 全部通过;困惑度全开、全关都是 3.5172。
  • 测试设备:MacBook Pro(M5 Max,40 核 GPU,128 GB),macOS 27.2,模型 Qwen3.8-27B Q4_K_M。

Possible Implementation

Code is on the metal-m5-prefill branch of LXD-8/llama.cpp, one commit per change, each behind an environment variable toggle:
https://github.com/LXD-8/llama.cpp/tree/metal-m5-prefill

Per CONTRIBUTING.md I plan to split this into one PR per change. Before opening any PR I would like to know:

  1. Is there interest in landing this work at all?
  2. If yes, is there a preferred order or grouping for the individual PRs?

代码在 LXD-8/llama.cpp 的 metal-m5-prefill 分支,每项改动一个提交,每项都有对应的环境变量开关:
https://github.com/LXD-8/llama.cpp/tree/metal-m5-prefill

按 CONTRIBUTING.md 的要求,我计划按项拆成多个 PR。在开任何 PR 之前想先确认:

  1. 这个方向是否有兴趣合入?
  2. 如果有,各子 PR 是否有偏好的顺序或分组?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions