针对M5 Max优化的Splash分支:速度提升约1.5倍(单请求1.25倍)
摘要
Splish是Splash的一个非官方分支,专为Apple M5 Max芯片优化Metal内核,能够在不降低质量的情况下,将AI推理速度提升高达1.5倍,适用于Qwen3.8-27B等模型。
查看缓存全文
缓存时间: 2026/09/27 07:42
publicExcess/splish 源码:https://github.com/publicExcess/splish
Splish
许可证: Apache-2.0 发布版 (https://github.com/publicExcess/splish/releases/tag/splish-v1.0) Ko-fi (https://ko-fi.com/severalviolins)
速度比原版 Splash 最高快 1.5 倍,单请求约快 1.25 倍,且质量未变:在 40 核 M5 Max 上运行 Qwen3.8-27B 系列 4 位模型。Splish 是 Inco 公司的 Splash (https://github.com/incoai/splash) 的非官方分支,与 Inco 无关联。它为苹果 M5 系列 GPU 重新调整并扩展了 Splash 的 Metal 内核,并在 40 核 M5 Max(128 GB,MacBook Pro)、macOS 27.0、Xcode 27.0(Metal 工具链 27A266a)上开发和测试。
为何创建分支。 Splish 的大部分改动都针对单一芯片和操作系统(macOS 27 上的 40 核 M5 Max)进行了调优。在相同芯片上使用 macOS 26.6 进行的独立运行已经对一些内核的排名产生了影响。与其要求 Inco 审查和维护可能无法移植到其他 Mac 上的硬件特定工作,并分散对引擎整体的注意力,不如让它在这里独立存在。硬件无关的部分(从文件加载内核选择、复制规则、基准测试工具)如果 Inco 需要,可向其上游提供。
对比原版 Splash 1.1.0,在同一台 Mac 上使用相同模型。每个单元格是解码 tok/s,原版 → Splish(增益);在 2–4 个请求时,是所有请求的总和。模型均为 4 位仿射格式(MLX 风格,分组 64,Splash 的包格式),除非标记为 GGUF,并使用 Splash 默认的 int8 KV 缓存和每个模型的 DFlash2 草稿。方法参见与原版 Splash 对比。
| 工作负载 | 1 个请求 | 2 个请求 | 3 个请求 | 4 个请求 |
|---|---|---|---|---|
| Inco 的 Qwen3.8-27B,长推理 | 131 → 178 (+35%) | 223 → 292 (+31%) | 222 → 330 (+48%) | 299 → 392 (+31%) |
| Inco 的 Qwen3.8-27B,短回答,贪心 | 78 → 99 (+26%) | 134 → 164 (+23%) | 138 → 171 (+24%) | 183 → 210 (+15%) |
| Inco 的 Qwen3.8-27B,短回答,采样 | 74 → 91 (+23%) | 118 → 151 (+28%) | 125 → 171 (+37%) | 163 → 209 (+28%) |
| Inco 的 Qwen3.8-27B,TensorFold 客户端:代码 | 142 → 176 (+24%) | |||
| Inco 的 Qwen3.8-27B,TensorFold 客户端:聊天 | 75 → 92 (+22%) | |||
| Inco 的 Qwen3.8-27B,2K–128K 令牌文档(按上下文长度) | +11% 到 +29% | 96 → 118 (+24%, 64K) | ||
| Swift-1.5(Qwen3.8-27B 的微调版),长推理 | 141 → 179 (+27%) | 224 → 296 (+32%) | 224 → 342 (+52%) | 288 → 400 (+39%) |
| Qwen3.6-35B-A3B,长推理 | 331 → 348 (+5%) | 486 → 573 (+18%) | 553 → 672 (+22%) | 642 → 754 (+18%) |
此外,复制规则加速了整个文件的代码编辑(Swift-1.5,Splish 不使用 → 使用):144 → 180 tok/s (+24%) 和 136 → 194 tok/s (+42%)。质量未变:上述所有模型在使用 Splish 时,在我们 95 个任务的测试集上得分均为 95/95,与我们测量过的原版版本一致。
按上下文长度
单个请求,总结给定长度的文档(一篇不同的 WikiText 段落),输出 2,048 个令牌。提示在解码计时开始前已预填充到缓存中。解码是 2 轮的平均值;预填充是冷启动的第一轮。Inco 的 Qwen3.8-27B,tok/s,原版 → Splish:
| 上下文 | 2K | 8K | 32K | 64K | 128K |
|---|---|---|---|---|---|
| 解码 | 92 → 109 (+18%) | 74 → 95 (+29%) | 72 → 80 (+11%) | 66 → 77 (+16%) | 54 → 63 (+16%) |
| 预填充 | 1,019 → 923 | 829 → 895 | 851 → 864 | 794 → 803 | 701 → 713 |
在每个长度下解码都更快。预填充速度未变:Splish 不涉及预填充内核,这些单次冷启动测量的差异在 −9% 到 +8% 之间。Splash 自身的基准工具也测量了 2K 和 32K 时的首个令牌时间。
按上下文长度解码 按上下文长度预填充
此分支在 Splash 1.1.0 基础上新增了:
- 针对 M5 张量单元的新型验证内核。 Split-K 内核一次性计算每个投影的行和(
SplitSums32),加上更轻量的屏障。它们在 2–4 个请求时带来了增益。 - 针对该芯片测量的内核选择(
tuning/目录),从文件(SPLASH_KERNEL_CHOICES)加载。它们带来了大部分单请求增益。原版 Splash 无法使用它们:其选择是编译时确定的,其调优器是开发者构建目标,不包含在 Homebrew 包中。 - 面向编码代理的复制规则(来自 TensorFold (https://github.com/ashhart/TensorFold))。当最后 16 个或更多令牌重复早期上下文时,下一个草稿是逐字的延续。输出是精确的:贪心文本是字节相同的,采样接受使用单热草稿分布。
- 更快的 GGUF 解码(G4a 分阶段内核),输出位相同:每个内核 +3–14%,每步约 2%。
- 确保数据真实性的工具。 包括一个与 fp64 对照的内核基准测试、一个带置信区间的整步基准测试、一个位对位构建比较,以及一个带功耗读取的稳态服务基准测试。
未提速的地方。 长上下文注意力本身未改变;64K 时的领先来自其周围的所有优化。Qwen3.6-35B 单请求仅增益 5%,GGUF 模型约 2%。在 3–4 个请求时每令牌能耗更低,但在 1–2 个请求时情况不一。原版领先的场景列出了所有案例。
其他一切都是 Inco 的:引擎、包格式、模型和草稿模型,以及服务器。此分支有意识地跟踪上游发布(目前为 Splash 1.1.0;upstream 远程仓库)。
快速开始
你需要一台运行 macOS 26.4+、Xcode 26 或更新版本以及 Python 3.12–3.14 的 Apple Silicon Mac。
git clone https://github.com/publicExcess/splish.git && cd splish
make -j4
./splish serve --model mlx-community/Qwen3.8-27B-4bit
首次 serve 会下载模型及其 DFlash2 草稿并准备权重,与 Splash 相同。./splish 会为模型选择调优后的内核选项并启用复制规则;它会打印所选内容。然后连接一个代理(./splish opencode、claude、codex、hermes、pi)或任何兼容 OpenAI 或 Anthropic 的客户端,与 Splash 类似(上游 README)。
调优后的选项是针对 macOS 27 上的 40 核 M5 Max 的。在 macOS 26.6 上另一台 40 核 M5 Max 上进行的独立调优运行为某些形状选择了不同的最优解;增益方向相同但幅度减半。这就是为什么选择应该针对每台机器进行调优。在任何其他 Mac 上,./splish 会保留 Splash 自身的默认设置,但仍会启用复制规则。其他 M5 芯片需要自己的选择;自动调优器正在开发中。
高级选项。 ./splish 仅在以下变量未设置时应用:
| 变量 | 效果 |
|---|---|
SPLASH_KERNEL_CHOICES=FILE | 要加载的内核选择(tuning/);未设置则使用 Splash 默认值 |
SPLASH_CHOICES=any | 在非 40 核 M5 Max 芯片上应用调优后的选择 |
SPLASH_M5_COPY_MIN_MATCH=N | 复制规则:当 N+ 个重复令牌时,草稿为逐字延续(默认 16;0 则关闭) |
SPLASH_M5_ACCEPT_LOG、SPLASH_M5_TOKEN_LOG | 诊断:接受直方图和每步令牌 |
推荐设置
| 模型 | 选择文件 | 备注 |
|---|---|---|
| Qwen3.8-27B 系列,Splash 包(Inco 的、Swift-1.5、其他微调版) | tuning/m5max-40c-swift15-v8.choices | 在 40 核 M5 Max 上调优。其他 M5 芯片:运行调优器(dev/tuning)。 |
| Qwen3.8-27B GGUF Q8_0 | tuning/m5max-40c-swift15-q80.choices | 约 2% |
| Qwen3.6-35B-A3B | tuning/m5max-40c-qwen36-35b.choices | 1–4 个请求时 +5/+18/+22/+18% |
| Qwen3.8-27B GGUF Q4_K_M, Q6_K | tuning/m5max-40c-swift15-kquant.choices | 约 2%(G4a 内核起主要作用) |
复制规则: 通过 ./splish 默认开启(SPLASH_M5_COPY_MIN_MATCH=16)。它在重写文件的代理上有效,在散文上从不触发,即使误触发最多也只增加约 3% 的开销。草稿长度:保持 Splash 的 7 个草稿令牌。在长推理负载下,50–62% 的验证步骤接受全部 7 个(在 Qwen3.6-35B 上为 45–48%)。将草稿截断为 5 只能保留每步约 81% 的令牌,这比缩短验证节省的更多。温度:在默认风扇行为下,持续 3–4 个请求的负载在两款引擎上 GPU 峰值温度达到 92–94 °C。对于长时间的代理会话,请设置风扇曲线,使其在 80–85 °C 时达到全速。
与原版 Splash 对比
方法。
- 构建版本。 原版是原生的 Splash 1.1.0。分支是此仓库,使用了 v8 选择。两者在使用相同包的同一台 M5 Max 上,通过不同端口并行运行。
- 服务负载。 C 个并发请求(C = 1–4),每个从长推理提示生成最多 4,096 个令牌,使用推荐的采样设置(温度 1.0,top_p 0.95,top_k 20)。
- 指标。 聚合解码 tok/s,仅在所有 C 个请求解码时计数(
dev/m5/serve_bench.py)。包功耗和 GPU 温度在同一时间窗口内通过macmon获取。 - 重复次数。 每个值是两轮的平均值,原版和分支交替进行。运行差异最高可达约 7%(单个单元格最高 8%),因此 5% 以下的差异视为持平。
- Splash 自身的基准工具。
dev/benchmarks/http_regression.py(ABBA 顺序,abba.py的通过规则)测量了 2K 和 32K 上下文时的每令牌解码毫秒数和首个令牌时间。 - 质量。 在两个构建版本上使用包含 95 个任务(数学、代码、推理、精确匹配评分)的测试集。
哪个构建版本。 长推理、质量和基准工具数据(2026-09-26 夜间运行)使用的是当前构建版本,包含 G4a 和 A4。512 令牌行和 Swift 的步进时间早于 G4a 和 A4。这两项更改都是位相同的,且仅影响 GGUF 模型和长上下文。
Splish 领先的地方
Inco 的 Qwen3.8-27B 包(incoai/Qwen3.8-27B-Splash),聚合 tok/s,分支与原版对比:
| 负载 | C=1 | C=2 | C=3 | C=4 |
|---|---|---|---|---|
| 贪心,512 令牌 | 98.8 +26% | 163.9 +23% | 170.8 +24% | 209.7 +15% |
| 采样,512 令牌 | 90.5 +23% | 151.3 +28% | 171.2 +37% | 208.6 +28% |
| 64K 令牌文档,总结,稳态 | 73.4 +12% | 118.2 +24% | ||
| 长推理,稳态,采样 | 177.7 +35% | 292.0 +31% | 329.5 +48% | 391.8 +31% |
| 每令牌能耗,J(原版 → 分支) | 0.65 → 0.36 | 0.24 → 0.30 | 0.25 → 0.22 | 0.21 → 0.19 |
单个请求,按模型 1-4 个并发请求 Inco 的 Qwen3.8-27B,原版 vs Splish,贪心 Inco 的 Qwen3.8-27B,原版 vs Splish,采样
原版参考值:贪心 78.2 / 133.7 / 137.8 / 182.6 tok/s,采样 73.8 / 118.2 / 124.9 / 163.4,长推理 131.3 / 223.2 / 222.2 / 299.0。长推理数学的草稿效果很好(每个验证步骤约 6.2 个令牌),这就是为什么它的 tok/s 比短回答更高。
Swift-1.5 在同一长推理负载下:140.5 / 224.2 / 224.1 / 287.7 → 179.0 / 295.8 / 341.5 / 400.2 tok/s (+27% / +32% / +52% / +39%)。4 个请求时能耗为 0.32 → 0.18 J 每令牌。
TensorFold 的客户端(tools/bench_openai.py 来自 ashhart/TensorFold (https://github.com/ashhart/TensorFold),通过 dev/m5/tensorfold_bench.py;64 个令牌回复,5 个种子,2 轮)。代码和聊天,采样与贪心,tok/s:
| 代码,采样 | 聊天,采样 | 代码,贪心 | 聊天,贪心 | |
|---|---|---|---|---|
| 原版 Splash 1.1.0 | 156.6 | 77.8 | 142.1 | 75.1 |
| Splish | 202.6 | 88.6 | 176.1 | 91.9 |
| TensorFold 0.3.4,其为 M5 Max 发布的版本 | 168.4 | 69.3 | 154.7 | 73.5 |
TensorFold 行是其自身发布的测量值:不同的检查点和草稿器、一个原始补全的代码提示和不同的会话。将其视为参考点,而非竞赛。
Splash 自身的基准工具(http_regression.py,ABBA,Inco 的通过规则)在 Inco 的 27B 上:解码 5.93 → 4.60 ms 每令牌 (−22%),通过。在 32K 时首个令牌时间相同(39.5 s 对比 39.4 s)。在 2K 时为 +0.7%,但该运行的分散度为 6.9%,超过了规则的 5% 限制,因此规则判定为不确定而非回归。
Swift-1.5(Qwen3.8-27B 的微调版,相同形状),使用 2,048 令牌提示时,来自 Splash 的 decode_profile 的解码步进时间:
| 请求数 | 原版 1.0.2 | 1.0.2 + 调优选择 | Splish (v8) |
|---|---|---|---|
| 1 | 49.0 ms | 41.5 ms | 40.5 ms |
| 2 | 59.1 ms | 59.4 ms | 44.9 ms |
| 3 | 87.2 ms | 88.2 ms | 59.3 ms |
| 4 | 87.0 ms | 85.8 ms | 62.9 ms |
在单个请求时,分支比仅使用调优选择增益约 2.5%。来自分支自身内核的增益在 2–4 个请求时(1.3–1.5 倍)。
质量。 Swift-1.5 在两个引擎上都得了 95/95,四个副本并发运行时得分 380/380。在 Inco 的 27B 上:原版 95/95,分支 95/95。分支的贪心文本与原版在接近平局时不同,因为 Split-K 内核的加法顺序不同。两个引擎在运行间都是确定性的。
原版领先或持平的地方
| 场景 | 结果 |
|---|---|
| 接受投机,Inco 的 27B | 分支 0.380 vs 原版 0.398(贪心,16 个提示)。略低;速度增益足以弥补。 |
| 长上下文解码(40K+) | 持平。注意力设定了限制,而唯一保留的更改(A4)约占注意力的 3%,每步不明显。 |
| GGUF Q8_0 解码,2 个请求 | 持平(−0.1%)。1、3 和 4 个请求:快约 2%。 |
| Qwen3.6-35B-A3B(调优) | 未经调优。分支的注意力更改因其形状而被禁用,在该形状下慢了 3%。 |
| 首个令牌时间,2K / 32K | 持平(+0.7% / −0.2%)。2K 运行噪声太大,未通过 Inco 的规则。 |
| Qwen3.6-35B-A3B,长推理,1–4 个请求 | 持平:−2% / −1% / −4% / +2%,在其 5–7% 的运行分散度内。 |
有效的方法
-
Split-K 验证内核,使用
SplitSums32一次性计算行和,在 MPP 张量单元上运行。残差、普通和门控/上投影在 16–32 行时增益,在 2–4 个请求时将步进时间缩短了 23–32%。 -
更轻量的屏障 (H1)。 当一个瓦片只有一个 simdgroup 时,使用 simdgroup 屏障替代线程组屏障。
-
针对每个形状测量选择(
v8),从文件加载。 -
GGUF 输入预取 (G4a)。 分阶段的 GGUF 瓦片现在将输入行提前一步预取到从其权重阶段释放的空间中。它使用相同的线程组内存,产生位相同的输出,在 32 行时快 2–8%。
GGUF Q8_0 在 32 行时
-
6 头组使用 4 个 simdgroup 的注意力 QK (A4)。 位相同;注意力快 2–3%。
-
使用相同内核调优第二个模型。 Splash 的
tune-kernels在 Qwen3.6-35B-A3B 上为其稠密投影选择了 31 个SplitSums32选择。这些选择在其形状上经过了 fp64 和竞态检查。在 1–4 个请求时服务增益为 +5/+18/+22/+18%。 -
复制规则(来自 TensorFold)。在构建之前进行了测量:重放真实对话记录(
SPLASH_M5_TOKEN_LOG、dev/m5/copy_rule.py)预测在两次整个文件编辑中增益 +26% / +42%;实际运行时为 +24% / +42%。第一个版本拒绝了所有复制。引擎首先发出每步的锚点令牌,因此复制提前了一个令牌;一个调试日志(SPLASH_M5_COPY_DEBUG)发现了这个问题。Swift-1.5,贪心,提示已缓存 关闭 开启 整个文件编辑(类型提示),tok/s 144.4 179.7 整个文件编辑(文档字符串),tok/s 136.3 194.2 短重命名,tok/s 197.8 196.4 散文 / 推理 −0.4 到 −0.7% 接受全部 7 个草稿的步数 28% 45%
一种相关且互补的方法: harryslimes 的上下文复制参考 (https://github.com/harryslimes/ninfer-fast/pull/1) 适用于 NInfer 和 Pi 代理的基准工具层。模型被指示发出简短的复制命令(``)而不是重新输入文本,基准工具展开它们。这在大规模重写时节省更多,但它改变了模型的输出格式并需要基准工具扩展。Splish 的复制规则保持模型的精确输出,并且不需要
相似文章
在40核 M5 Max 上的 Splash:通过调优内核为你的芯片实现解码性能提升20%
本文介绍了如何在 Splash 中使用 'make tune-kernels' 工具来优化40核 M5 Max 芯片上的 AI 模型解码,通过为特定硬件调优内核布局,实现高达20%的速度提升。
Qwen3.8-27B在M5 Max MacBook Pro上实现144 tok/s速度
Inco Splash是一款针对Apple silicon优化的开源推理引擎,在M系列MacBook上运行Qwen3.8-27B等AI模型时提供了显著的速度提升。
@jianchen1799: 本地模型现在可以处理智能体工作负载。本地推理引擎需要迎头赶上。今天我们发布 Splash: …
Inco AI 发布 Splash,一款针对 Apple silicon 优化的开源推理引擎,声称解码速度最高提升3倍,用于本地模型服务,使得 M5 Max MacBook Pro 等设备能够支持智能体工作负载。
Splash 1.1.0 发布,支持 GGUF 量化、MLX 导入等更多功能
Splash 1.1.0 已发布,支持 GGUF 量化、MLX 导入以及针对 Apple Silicon 优化的内核,从而实现高速高效的本地 AI 推理。
适用于 Apple Metal 的 Automatic1111,sd1.5 速度提升 40%
本文介绍了一个专为 Apple Silicon 优化的 Automatic1111 分支,加入了 Metal 优化(例如 Metal Flash Attention),以加速 Stable Diffusion 1.5 的生成速度,在 M3 Pro 上将时间从 8-10 秒缩短至 3-7 秒,在 M1 Mac Mini 上从 13-20 秒缩短至 8-10 秒。