开源llama.cpp Releases·原文 2026年9月14日本站收录 2026年9月15日

llama.cpp b10956为SYCL后端加入大k值radix select,k>32 不再回落CPU

该版本用基数选择替代扫描归并,解决SYCL后端GGML_OP_TOP_K在k大于 32 时被迫退回CPU的限制;qwen4exp稀疏注意力索引器每token在 12 层请求k=2048,此前每次都会触发这一回退。

AI解读:llama.cpp的b10956版本给SYCL后端补上了一块长期短板:TOP_K运算在k大于 32 时不再把数据送回CPU处理。此前这限制并非出于保守,扫描归并kernel要在共享本地内存里保留 (split_block + 1) * k个候选对,k=128 时一个工作组就需要 132 KB,根本没法启动。

真正受影响的是在Intel GPU上跑带稀疏注意力机制的模型的人。qwen4exp的索引器要求每token在 12 层里做k=2048 的top-k,旧实现下每一次都会触发CPU回退。改用radix select后,共享内存只放直方图而不是候选集,占用与k无关。

性能取舍写得很具体:按行分配工作组会让设备在行数少于核心数时空闲,batch size 1时只有一个工作组,所以作者把一行拆到多个组上。实测ne=[200000,1] 和ne=[200000,16] 耗时 358.0 微秒和 363.4 微秒,多出 16 行只增加 1.5% 墙钟时间。

测试覆盖也补了空白:test-backend-ops的TOP_K从原先所有k>32 用例被拒,变成 525/525 通过,MUL_MAT_ID 880/880通过。作者报告wikitext-2困惑度在 512 和 81920 上下文下均在噪声范围内不变。

一个仍受限制的地方是适用边界:作者称在宽度 2 到 200K列、行数 1 到 8192 的实测范围内,radix select从k=8 起领先,k更小时仍落后,因此 64K列以下的小k场景仍由原单组kernel负责。

这批改动随b10956的macOS、Linux、Windows、Android二进制和UI包一同发布,SYCL用户可直接下载对应平台压缩包。

llama.cpp发布b10956版本,其主要改动是让SYCL后端用radix select处理大k值的TOP_K运算。此前SYCL后端拒绝k大于 32 的GGML_OP_TOP_K,并让其回落到CPU,导致每次调用都要做一次后端往返。

改动说明由提交信息给出:扫描归并kernel原先要在共享本地内存(SLM)中保留 (split_block + 1) * k个候选(值, 索引)对,k=128 时一个工作组就需要 132 KB,无法启动。qwen4exp的稀疏注意力索引器在每个token的 12 层里请求k=2048,因此该限制在每个上下文长度下都会触发。

新实现用四轮最高位优先的基数选择,在保序无符号键上寻找第k大:先对候选集的数字做直方图,从高位桶向下走,在累计计数达到所需数量的桶中递归。SLM只保存直方图而非候选,因此占用与k无关。最后一轮输出所有超过枢轴的列,并补齐与枢轴相等的列,使重复键仍能产出恰好k个不同索引。

键把 -0.0 折叠到 +0.0,使其等价类与参考比较器一致,在该比较器下二者相等。参考实现中NaN没有定义顺序(其比较器不构成严格弱序),此处 +NaN排在 +inf之上、-NaN排在 -inf之下,作者称这至少使结果具有确定性。

输出顺序没有被要求,也没有为此付费:ggml-cpu/ops.cpp交换了前两个输出以表明这一点。

把一行拆到多个工作组,解决行数不足导致的设备空闲

每个工作组负责一行时,当图的列数少于设备核心数,设备就会空闲;batch size为 1 时只启动一个工作组。qwen4exp对形状为 [n_kv, n_tokens/n_stream, n_stream] 的张量做top-k,token生成时nrows == 1,后端采样器也会把logits重塑为单行。

实测ne=[200000,1] 和ne=[200000,16] 分别耗时 358.0 微秒和 363.4 微秒,十六行只多出 1.5% 的墙钟时间。

跨遍状态移入全局内存,每遍独立启动

由于工作组屏障无法跨越行,每遍的状态移到全局内存,每个数字遍成为独立的启动。各组在SLM中累加,每次贡献 256 个全局原子操作,使全局流量按组而非按元素计算。

一行中最后一组——即fetch_add返回G-1的那组——执行该遍的扫描,把启动次数保持为每个数字一次加一次输出。组数来自设备并除以nrows向下取整,因此已经覆盖设备的行数不会被拆分,也不产生额外开销。

低于 64K列时,单组kernel能在额外启动开销之内完成,因此仍由它负责。

减少设备作用域原子读取的开销

通过设备作用域atomic_ref读取该行的prefix/mask/need比它所保护的扫描开销更大:这些加载未被缓存,导致第 2 到第 4 遍耗时 49 微秒,而第 1 遍为 12 微秒。改为由一条lane读入SLM,组内从那里取值后,在ne=[131072,1]、k=2048 时从 208 微秒降到 44.6 微秒。

块大小改为取设备的max_work_group_size,而不再封顶为 512。该上限从来不是下限,报告 512 的设备不受影响;允许 1024 的设备此前只被给予一半宽度。

扫描归并门限放在两条路径实际交叉处

扫描归并kernel的开销随k上升,而radix select的不随k上升。在宽度 2 到 200K列、行数 1 到 8192 的实测范围内,radix从k = 8 起领先,在k更小时落后。

作者称在d=131072 时约为 6.05 t/s,更浅的深度下二者相当。wikitext-2上的困惑度在 512 和 81920 上下文下均在噪声范围内不变。

test-backend-ops结果为 525/525 TOP_K(此前所有k > 32 的用例都被拒绝)、880/880 MUL_MAT_ID。性能覆盖新增了大宽度下k > 32 的情况以及短行的边角情况,这两者此前都未被覆盖。

该实现被移动到topk-radix.{cpp|hpp} 文件中。

b10956随附的平台包

该版本提供macOS/iOS(Apple Silicon arm64、Intel x64、iOS XCFramework)、Linux(Ubuntu x64/arm64/s390x CPU、Vulkan arm64/x64、ROCm 10.0、OpenVINO 2026.3.1、SYCL FP32与FP16)、Android arm64 CPU、Windows(x64/arm64 CPU、OpenCL Adreno arm64、CUDA 12.4/13.3/13.4、Vulkan、OpenVINO 2026.3.1、SYCL、ROCm 10.0)以及UI包。

发行说明中,macOS Apple Silicon的KleidiAI启用版与openEuler各包(x86 310p/910b ACL Graph、aarch64 310p/910b ACL Graph)标记为DISABLED。

信息来源

llama.cpp Releases原始来源