news 2026/9/12 13:20:05

colibri Vulkan 计算后端完全指南:在任意 Vulkan 1.2 GPU 上运行 GLM 解码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
colibri Vulkan 计算后端完全指南:在任意 Vulkan 1.2 GPU 上运行 GLM 解码

colibri Vulkan 计算后端完全指南:在任意 Vulkan 1.2 GPU 上运行 GLM 解码

【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri

colibri 内置了可选的 Vulkan compute 后端,能够在任何被 Vulkan 驱动可见的 GPU 上运行完整的 GLM 解码计算路径——不需要 CUDA,也不需要 ROCm。本文以 docs/vulkan.md 为主体,结合仓库源码与测试,系统讲解该后端的构建方法、环境变量、GPU 上执行的部件划分、正确性验证、实测性能、跨后端对比陷阱与已知限制,帮助你在被厂商堆栈放弃的旧卡、AMD 新卡或 APU 上榨出可用的推理性能。

快速上手:构建与运行

Vulkan 后端是**可选(opt-in)**特性,默认构建完全不受影响。构建命令在c/目录下执行:

cd c make glm VK=1 # needs libvulkan + glslc (shaderc) for the shaders

在 c/Makefile 中,VK=1会追加-DCOLI_VULKAN编译宏与-lvulkan链接选项,并把四份 GLSL 着色器编译产物纳入构建:

VK ?= 0 GLSLC ?= glslc ifeq ($(VK),1) CFLAGS += -DCOLI_VULKAN LDFLAGS += -lvulkan VK_OBJ = backend_vulkan.o VK_SPV = shaders/qmatmul.spv shaders/qmatmul_gate_up.spv shaders/attention_absorb.spv shaders/rmsnorm.spv endif

其中.spvglslc --target-env=vulkan1.2从同目录的.comp源码生成(Makefile 规则),构建时若找不到glslc会直接报错提示安装 shaderc。

运行一个最简单的例子:

COLI_VULKAN=1 COLI_VK_DENSE=1 COLI_VK_ATTN=1 \ PIN=<model>/.coli_usage PIN_GB=0 COLI_NO_OMP_TUNE=1 \ ./coli run "Hello" --topp 0.7

环境要求

  • 运行时libvulkan以及一块提供 Vulkan1.2ICD 的 GPU,驱动需支持GL_KHR_shader_subgroup_arithmetic扩展(近几年的任意 Mesa RADV、AMDVLK、NVIDIA 或 Intel ANV 驱动均满足)。
  • 构建时glslc(来自 shaderc)。
  • 后端会挑选能力最强的物理设备(独立显卡优先于核显),并且任何失败都会降级到 CPU 路径——一块卡死(wedged)的 GPU 最多拖慢运行速度,绝不会损坏结果。

别忘了 COLI_NO_OMP_TUNE=1

在多核机器上必须设置COLI_NO_OMP_TUNE=1:引擎的 OMP 自调优(自旋等待)在COLI_CUDA/COLI_METAL下会被跳过,但在 Vulkan 下不会。自旋的工作线程会饿死异步 I/O 池——实测 CPU 专家带宽会从 28 GB/s 跌到 5 GB/s。

独立显卡必须先开 Resizable BAR

权重分层分配的是HOST_VISIBLE|DEVICE_LOCAL内存;如果关闭 ReBAR,这种组合只存在于约 256 MB 的 BAR 窗口内,驱动会把超出部分静默放到系统内存里——于是分层报告专家已驻留,但每次访问都在穿越 PCIe,比 CPU 路径还慢。在 RX 9070 XT 上实测,BIOS 开关两侧分别是 0.11 与 0.24 tok/s。引擎在初始化时如果发现 VRAM 的可主机访问切片偏小会打印警告;看到该警告就去 BIOS 里开启 Resizable BAR / Smart Access Memory。统一内存(unified-memory)的 APU 不受影响。

着色器查找路径

编译好的着色器通过COLI_VK_SHADERS定位,可以是qmatmul.spv文件本身,也可以是存放整套.spv的目录;未设置时引擎先找二进制旁的shaders/,再找相对当前工作目录(CWD)的位置。注意 docs/ENVIRONMENT.md 的说明:给出目录时,其余着色器会在同一目录下按文件名被发现。

GPU 上跑什么:三块部件

后端把解码路径按“驻留频率”划分成三块,用三个环境变量独立开关:

部件环境变量机制
路由专家(热集)COLI_VK_EXPERTS=N(默认 320).coli_usage热度取 Top-N 专家,启动时一次性上传到 VRAM 注册表;解码时直接从 VRAM 提供,不占 RAM 槽、不读磁盘、不预取,以一次异步融合批次(gate+up+silu→down,隐藏层留在设备上)与 CPU 计算其余专家并行。命中率行中显示为vk桶。
稠密投影COLI_VK_DENSE=1q_a+kv_a 融合为一次提交,q_b、o 各自成批;共享专家作为单个融合专家组提交。驻留的 int4/int8 权重只上传一次。
MLA 注意力核心COLI_VK_ATTN=1每层一次 dispatch:吸收式 query(absorbed query)、KV 窗口上的分数、softmax、加权潜变量、值行,与 o 投影融合(上下文向量从不离开 GPU)。潜变量/rope KV 存放在持久化的逐层设备镜像中,每 token 每层追加约 2.3 KB,失效点与 CUDA KV 影子完全一致。

示例命令中的PIN_GB=0(但保留PIN)是有意为之:VRAM 注册表里已经装着与 RAM pin 相同的热专家,pin 的 RAM 应该留给自适应 LRU 缓存。保留PIN是为了防止 AUTOPIN 从历史记录里重新 pin。

这些开关背后是 c/backend_vulkan.h 中的一组 API 契约:

  • coli_vk_matmul:单个量化 GEMV,fmt=1为 int8、fmt=2为 int4,首次调用上传权重与 scale,之后复用驻留副本;
  • coli_vk_gate_up:专家 MLP 前半段(silu(gate(x))*up(x))在一次 dispatch内完成,x 只读一次;
  • coli_vk_expert_group/_issue/_take:整批专家的融合 MLP 一次提交(同步或异步,隐藏层全程在设备上);异步形态先下发再 join 读回,CPU 与 GPU 重叠计算;
  • coli_vk_attention_absorb/_project:吸收式 MLA 注意力核心,以及融合 o 投影的变体;
  • coli_vk_attn_qprep:q 预备链(q_a+kv_a 对 → q 潜变量 rmsnorm → q_b)在一次提交内完成,需要rmsnorm.spv
  • coli_vk_alloc_priority/coli_vk_mem_budget:借助VK_EXT_memory_priority/VK_EXT_memory_budget做 VRAM 压力防护——引擎把批量专家层填充的优先级压到 0.4,使过载的堆优先逐出冷专家,绝不动逐 token 的注意力工作集。

着色器层面的实现细节

c/shaders/qmatmul.comp 是解码 GEMV 的核心,注释明确列出了借自 llama.cppmul_mat_vec的三项技术:

  1. x[s,:]加载一次到共享内存,供工作组内每个输出行复用(x 位于 host-visible 暂存区,避免每次跨 BAR 重读);
  2. 一个 subgroup 对应一个输出行:lane 以完全合并(coalesced)的方式跨过权重字,用一次subgroupAdd取代屏障密集的共享内存树归约;
  3. 按行网格步进(grid-stride),固定且利于占用率的工作组数量可覆盖任意 O 与任意 subgroup 宽度(RADV wave32/64)。

着色器还实现了多种量化格式:int8、int4(每字节 2 个、nibble−8)、int3-g64(每 64 输入一组、双平面打包、每组一个 f32 scale)、分组 int4,以及 Kimi K3 专家使用的MXFP4(e2m1 nibble、bit3 为符号位,scale 由宿主预展开为 f32,每 32 输入一组)。隐藏维上限 6144(24 KB LDS);超过暂存数组长度的行(如o_projI = H*vh = 16384)会跳过共享内存暂存,直接从存储缓冲读取。

c/shaders/attention_absorb.comp 则演示了“单次 dispatch 跑完整注意力核”:每个(query 行 s, head h)一个工作组,吸收式 query、因果分数、softmax、潜变量上下文、值行读取全部在一次提交内完成,L/R 直接读设备端持久缓存。共享内存数组的硬上限为 Q≤256、R≤64、K≤512。

内存类型的两个写合并规则

实测中能让这套设计成立的是两条内存规则(见 c/backend_vulkan.c 中memtype/memtype_cached的选择):

  • CPU 要读回的缓冲必须是HOST_CACHED(否则 ReBAR VRAM 读取只有约 40 MB/s);
  • 其余一切使用HOST_VISIBLE|DEVICE_LOCAL——在 Strix Halo 这类统一内存平台上,所谓“上传”写进的正是 iGPU 读取的同一块物理 RAM,根本没有 PCIe 拷贝,这正是流式专家在这里能盈利而离散 CUDA 路径刻意回避的原因。

第二设备(COLI_VK_DEV2)

后端还支持把专家层放到第二块 Vulkan GPU 上:COLI_VK_DEV2可设为设备枚举索引或auto(自动选择非 0 号设备的最强真实 GPU),其上下文只承载层专家、只跑异步专家组路径,可与 0 号设备同时在途(backend_vulkan.h)。tensor 会记住自己所在的设备,free/bytes对两者皆可用。

正确性:如何在两个实现之间建立信任

Vulkan 路径与 CPU 路径是同一乘法的两套独立实现,唯一重要的是给出相同的 token:一个解错 nibble、搞反分组方向或打乱 scale 顺序的 kernel 不会报错,它只会给出一个变差的模型。因此验证策略是“比 token,不比 logit”。

1. 原语级精确性测试(文档给出的命令):

gcc -O3 -DVK_TEST backend_vulkan.c -o test_vk -lvulkan -lm && ./test_vk shaders/qmatmul.spv

该测试覆盖每个原语:不同形状下的 GEMV int4/int8(含长行 o 投影)、融合 gate+up、完整专家组的同步与异步路径、matmul 对,以及吸收式注意力核心(含因果 S=2、kv_start 窗口、int8 与长上下文)。典型 maxrel 约 1e-5 到 2e-3(fp32 归约顺序差异)。

2. 引擎级一致性:贪婪解码在验证提示词上与纯 CPU 引擎逐 token 完全一致。

3. 权重布局零重打包:int4 权重以偏移二进制(nibble−8)解码,与 CPU 路径的字节级布局完全一致,无需重新打包。

仓库里还有自动化回归测试 c/tests/test_glm53_vulkan.py,它用同一 fixture 分别以纯 CPU 与COLI_VULKAN=1运行引擎各 4 个贪婪 token,比较两者的输出;当二进制未以VK=1构建或没有可用设备时测试声明为SKIP而非假绿色。运行方式:

make VK=1 glm53 python3 tests/test_glm53_vulkan.py --binary ./glm53 --fixture ~/glm53_mm_tiny

实测性能(AMD RX 9070,RDNA4,RADV/Mesa 26.1)

文档给出的数据全部来自同一张 RX 9070 卡(RADV/Mesa 26.1):

指标Vulkan对比对象
专家 MLP 原语(K 个专家,int4 6144→2048→6144,含回读)0.11–0.13 ms/专家生产 ROCm/HIP 专家组0.179 ms/专家,快约 35%
解码 MLA 注意力核心3.7×快于同卡 HIP kernel
端到端 GLM-5.2(744B int4,NVMe 流式)12 核 Zen2 + RX 90701.7–1.8 tok/s(64-token)/ 1.6(256-token)/1.58 持续(512-token)HIP 后端同配置 1.5–1.55

其中“每调用含回读”意味着 0.11–0.13 ms 已经算上数据从设备回到 CPU 的开销。需要强调的是,端到端 tok/s 取决于整机还有什么在拖后腿(磁盘带宽、CPU 侧专家计算等),原语快 35% 不等于端到端同比例提升。

与其他后端对比时的两个坑

文档特别警告:有两个默认行为会静默扭曲任何 Vulkan 对 CUDA/HIP 的对比:

  1. MTP 投机解码:CUDA/HIP 构建默认禁用模型草稿(DRAFTCOLI_CUDA=1下自动解析为 0,见 issue #163),而 CPU 与 Vulkan 运行保持DRAFT=3。于是两组对照跑的是不同的解码循环——投机分支每个产出 token 要路由约 2× 的专家位置(被拒绝的草稿位置仍然付出专家 I/O),这在存储受限的机器上起主导作用。两边输出其实完全一致(贪婪验证无损),所以看起来没有任何异常。必须在两个分支上显式钉住DRAFT=0(或DRAFT=3 COLI_CUDA_MTP=1
  2. GPU 时钟:解码 dispatch 是微秒级突发,本身不足以让 DPM 提频,显存时钟可能整场运行都停在低位。两个分支都要钉住power_dpm_force_performance_level=high,或者在结果中如实披露时钟状态。

另外注意:运行统计里的experts loaded/token统计的是路由位置数(含被拒绝的投机位置),在咨询任何缓存/分层之前计数——VK 层命中时它不会下降;命中率行里的vk桶才是衡量分层有效性的数字。

环境变量速查

除文档正文外,docs/ENVIRONMENT.md 收录了完整的 Vulkan 相关变量,补充若干重要项:

  • COLI_VK_DEV:指定主 Vulkan 物理设备枚举索引;未设置时优先独立显卡。
  • COLI_VK_QPREP(默认开):把 Q 预备步(RMSNorm + rope + compress)融合为一次 GPU dispatch 而不是拆开(拆开会付出三个 fence 的代价);2额外保留 CPU 参考副本用于 A/B 对比。
  • COLI_VK_RESERVE_GB(默认 3.0):为惰性分配的稠密权重、KV 镜像与暂存缓冲预留的 VRAM(4k 上下文实测约 1.7 GB,随max_t增长)。仅在驱动报告VK_EXT_memory_budget时有意义,否则只受COLI_VK_EXPERTS数量上限约束。
  • COLI_VK_SPIN_US(默认 300):轮询 fence 的微秒数;0表示总是阻塞(空闲时延迟更低,代价是一个核心空转)。
  • COLI_VK_EXPERTS2/COLI_VK_RESERVE2_GB:dev2 层的专家数上限与保留 VRAM。
  • VK_PROF:设置后计时并上报 Vulkan 专家组路径。
  • Kimi K3 模型另有两个变量:K3_VK_GB(K3 专家层的 VRAM 上限,默认取驱动预算)与K3_VK_UP(填层时每步允许的专家上传数,默认 8)。
  • COLI_VK_TEST_BALLAST:分配 N 个哑 Vulkan 缓冲以复现“VRAM 空闲时解码注意力仍随专家层规模退化”的现象——这是VK_EXT_memory_priority存在的理由(实测 2.6k 缓冲对象 7.9s → 4.3k 时 15.6s,且仍有 2.9 GB 空闲)。

从源码结构看(backend_vulkan.c),后端还维护了一个重提交缓存:当绑定的 tensor/形状/暂存缓冲与上次调用一致时,跳过vkUpdateDescriptorSets与命令重录——这正是热专家被反复调用的典型模式;由于每次调用都会同步等待 fence,不会有提交在途,因此这种“仅在变化时重绑”是安全的。同一段代码也记录了注意力工作集(KV 镜像、暂存)借助 memory-priority 高于批量专家权重的设计依据:实测驻留 7.6 GB 时解码注意力耗时 7.8s,涨到 15.2 GB 时变成 17.8s。

已知限制与未来工作

  • 解码聚焦:专家层与注意力核心服务于S<=4;prefill 使用 CPU/批处理路径(稠密投影在 prefill 阶段可在 VK 上运行)。
  • 回退到 CPU 注意力路径:DSA top-k 选择、参差不齐(ragged)的多槽服务、以及量化 KV 缓存。
  • 尚未完成:cooperative-matrix(coopmat)prefill kernel、全驻留层流水线、在真实硬件上验证 Polaris/gfx803(着色器使用动态 subgroup 大小,按构造即 wave64 安全)。

小结

Vulkan 后端让 colibri 的“tiny engine, immense model”理念第一次与硬件厂商无关:无论是 ROCm 已放弃的 Polaris(RX 580 经 RADV 可跑),还是 RDNA4 新卡(实测快于同卡 ROCm/HIP),只要驱动提供 Vulkan 1.2 与 subgroup 算术扩展就能接入。配合COLI_VK_EXPERTS的驻留专家层、融合的注意力核与异步专家组,它把“从磁盘流式拉专家”的瓶颈从 I/O 转移到了可并行的 GPU 计算上。动手前请务必确认三件事:构建机有glslc、运行时设置COLI_NO_OMP_TUNE=1、独立显卡开启 Resizable BAR。

【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:19:59

AI全栈开发实战:从模型选型到Agent编排的完整链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:18:56

基于YOLOv7的跌倒检测实战:从数据集制作到实时监控部署

简介&#xff1a;基于Python的YOLOv7人员跌倒检测系统是一套可直接运行学习的完整项目&#xff0c;面向计算机视觉初学者、安防监控开发人员及养老机构/公共区域安全管理者。资源打包了源代码、教程和专用数据集&#xff0c;能帮助读者快速掌握YOLOv7在跌倒识别场景中的落地方案…

作者头像 李华
网站建设 2026/9/12 13:15:10

工业项目为何偏爱TCP以太网温湿度传感器?选型与调试实战解析

我当时在一个自动化改造项目的选型会上&#xff0c;甲方工程师反复强调一句&#xff1a;“温湿度传感器必须是TCP协议、以太网口&#xff0c;最好带Modbus TCP。”底下有人小声嘀咕&#xff1a;不就是测个温度和湿度嘛&#xff0c;RS485不也一样能读&#xff1f;现场那么长一段…

作者头像 李华
网站建设 2026/9/12 13:14:50

Python正则表达式re模块核心功能与优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华