Module 5:GPU Compute API 版图
理解 kernel、buffer、command queue、同步这些共通概念,并比较 CUDA、Vulkan、Metal、OpenCL、Direct3D 12 + DirectML 的取舍。
学习目标
- 抽象出所有 GPU compute API 的共同模型:buffer、kernel、dispatch、queue、barrier、fence。
- 理解 CUDA 为什么是深度学习事实标准,以及它的 vendor lock-in 代价。
- 理解 Vulkan / SPIR-V 的跨平台能力与复杂度。
- 理解 Metal、OpenCL 的定位。
- 解释为什么本项目选择 Direct3D 12 + DirectML,并为什么仍要设计 HAL。
5.1大图景:所有 API 都在表达同一件事
CUDA、Vulkan、Metal、OpenCL 与 Direct3D 12 的术语和对象体系不同,但它们表达的计算流程相近:分配或上传 buffer,编译 kernel/shader,建立资源绑定,向 command queue 提交 dispatch,再用 fence、event 或等价机制确认 GPU 工作完成。理解这条公共路径,比记忆某个 API 的函数名更重要。
5.2CUDA:深度学习的事实标准
CUDA 是 NVIDIA 的专有 GPU compute 平台,也是深度学习中最成熟的工程生态之一。其优势不只来自 kernel 语言本身,还来自 cuBLAS、cuDNN、NCCL、TensorRT、Nsight 以及主流框架长期优化出的后端路径。代价同样明确:程序与 NVIDIA 硬件和 CUDA 运行时绑定。
| 优点 | 代价 |
|---|---|
| 生态成熟,资料丰富 | 绑定 NVIDIA GPU |
| cuBLAS/cuDNN 性能极强 | Windows/Xbox/AMD/Apple 路径不适用 |
| 调试和 profiling 工具完善 | 跨平台抽象需要额外层 |
| 很多论文和 kernel 以 CUDA 为参考实现 | 教学上容易把 matmul 直接交给库,隐藏细节 |
若目标是在 NVIDIA GPU 上尽快获得高性能,CUDA + cuBLAS 是合理方案。本教程的目标包括 Windows / Xbox 路径、D3D12 资源模型和 custom kernel 机制,因此不能把 CUDA 作为唯一后端。
5.3Vulkan compute 与 SPIR-V
Vulkan 是跨厂商、跨平台的低层图形与计算 API,shader 通常编译为 SPIR-V。它覆盖 Windows、Linux、Android 以及 NVIDIA、AMD、Intel 等硬件,适合构建可移植 backend。相应地,Vulkan 把设备选择、内存分配、descriptor set、pipeline、barrier 和 queue family 等细节显式交给程序管理,代码量和概念负担较高。
Vulkan 适合构建跨平台底层 backend,但教学门槛比 D3D12/HLSL 更高;而且 Xbox 原生路径不是 Vulkan。
5.4Metal 与 OpenCL:作为背景
Metal 是 Apple 平台的现代 GPU API,在 macOS / iOS 上具有统一的工具链和较低的集成成本,但不覆盖 Windows / Xbox。本项目主线面向 Windows PC 与 Xbox GDK,因此不会以 Metal 为实现基础。
OpenCL 曾长期承担跨平台通用计算角色,概念上有助于理解 GPU compute 的历史演进。不过,在深度学习生态、调试工具和现代图形 API 互操作方面,它已不是本教程的最佳主线。
5.5Direct3D 12 + DirectML:本项目的选择
xinfer 选择 Direct3D 12 作为资源与执行基础,并使用 DirectML 研究 operator 生命周期,同时为 transformer 主路径保留 custom HLSL kernel。这个选择来自以下工程约束:
- **Windows 原生:**目标机器是 Windows PC,D3D12 是第一等公民。
- **Xbox 路径:**Xbox GDK 使用 D3D12 风格设备创建;host 可以把设备注入 Rust core。
- **HLSL 教学友好:**HLSL compute shader 语法直观,适合讲 threadgroup、SRV/UAV、barrier。
- **DirectML 可作为 operator API:**DirectML 展示了 create → compile → initialize → bind → dispatch 的算子生命周期;核心 transformer 路径仍可改用自写 HLSL kernel。
项目源码同时保留 DirectML operator 封装和 custom HLSL kernel 路径。实际工程中,库算子在特定 shape、驱动或硬件组合下可能出现不可接受的行为;此时能够退回到自写 kernel,是推理引擎必须具备的能力。
5.6可移植性策略:为什么要建 HAL?
模型层不应直接依赖 ID3D12Resource、root signature 或 descriptor heap。HAL(Hardware Abstraction Layer)把模型需要的能力收敛为更稳定的 Device、Buffer、Kernel / Operator 接口,使模型结构、runtime 与具体 GPU API 分离:
在 xinfer 中,xinfer-backend 定义 HAL 边界,xinfer-dml 在其后实现 D3D12/DirectML backend。DmlDevice::from_existing 与 from_raw_pointers 允许外部 host 注入已创建的 ID3D12Device 和 command queue;这对应 Xbox GDK 由 native host 创建设备,再交给 Rust core 使用的路径。若未来加入 Vulkan backend,应替换的是 backend crate,而不是 Qwen 模型的前向逻辑。
5.7如何选择 backend?一个工程决策表
| 候选 | 适合场景 | 不适合本项目的点 |
|---|---|---|
| CUDA | NVIDIA-only,高性能深度学习 | 不能覆盖 AMD / Xbox;vendor lock-in |
| Vulkan | 跨平台、跨厂商底层 backend | 学习曲线陡;Xbox 不是原生 Vulkan 路线 |
| Metal | Apple 平台最佳体验 | 不覆盖 Windows/Xbox |
| OpenCL | 历史上的跨平台 compute | 生态与现代调优体验较弱 |
| D3D12 + DirectML | Windows / Xbox;HLSL;可注入 host-created device | 非 Windows 平台不可用;某些 DML operator 可能有坑 |
本教程选择 D3D12 + DirectML,是因为它与项目目标匹配:Windows 原生可运行,Xbox 路径清晰,HLSL 便于展示 threadgroup、SRV/UAV、barrier 与 dispatch,底层对象也足够透明。
小结
本章比较了几类 GPU compute API。CUDA 代表最成熟的深度学习生态,但与 NVIDIA 平台绑定;Vulkan 提供跨厂商底层能力,但显式管理成本高;Metal 适合 Apple 平台;OpenCL 更适合作为历史背景。xinfer 的当前路线是以 D3D12 管理资源、命令队列和同步,以 DirectML 理解 operator 生命周期,并在核心 transformer 算子上使用 HLSL kernels。HAL 的作用是让模型与 runtime 依赖抽象设备和 buffer,而不是依赖某个具体 API;因此将来可以把 xinfer-dml 替换为 Vulkan 等 backend,而不重写模型结构。
Lab 5概念比较:两个 API 的 Hello Compute
选择 CUDA、Vulkan、Metal、D3D12 中任意两个,比较它们完成 out[i]=in[i]*2+1 需要哪些概念步骤。
不要求写代码,但要画出概念流程。
- 如何创建 device / context?
- 如何分配 GPU buffer?
- kernel / shader 用什么语言写?编译成什么格式?
- 如何绑定 input/output buffer?
- 如何提交 dispatch?
- 如何等待完成并读回结果?
思考与练习
基础列出所有 GPU compute API 都需要表达的 5 个共同概念。
① 设备/队列(device & command queue,提交工作);② 缓冲区/资源(buffer/memory,存数据);③ kernel/shader(要执行的程序);④ 资源绑定(descriptor/binding,告诉 kernel 用哪些 buffer);⑤ dispatch + 同步(启动 N 个线程组并用 fence/barrier 同步)。无论 CUDA、D3D12、Vulkan、Metal,都要表达这五件事。
基础为什么 CUDA 在深度学习中很强,但不适合作为本教程唯一 backend?
CUDA 生态成熟、kernel 库丰富,但它只支持 NVIDIA GPU。本教程目标包括 Windows PC 与 Xbox GDK 路线,不能把 NVIDIA-only 后端作为唯一实现。D3D12/DirectML 更符合这些平台约束;同时保留 HAL,可避免模型层被某个后端锁定。
进阶解释 descriptor / resource binding 在 D3D12 或 Vulkan 中解决了什么问题。
它解决“shader 如何知道该访问哪块 GPU 内存”的问题。shader 里只写逻辑寄存器槽(如 register(u0)),descriptor 把这些槽映射到具体的 GPU 资源(buffer/texture 的地址、格式、范围)。这层间接让同一个 shader 可在不同帧绑定不同资源,并让驱动统一管理资源生命周期与访问权限。xinfer 用 root descriptor 直接绑定 buffer 地址,省去描述符堆重绑。
进阶如果模型代码直接依赖 D3D12 COM 类型,会怎样影响未来移植?
模型层会和 Windows/D3D12 紧耦合:想换 Vulkan/Metal 或上别的平台就得改 Qwen 前向逻辑本身,而不仅是 backend。这违背关注点分离。正确做法是把 D3D12 细节藏在 backend trait(Device/Buffer/Kernel)后面,让 xinfer-model 只依赖抽象接口,移植时只替换 xinfer-dml 这一层。
挑战为 xinfer 设计一个假想 Vulkan backend 的 crate 边界,说明哪些 trait 可以复用。
新增 xinfer-vk crate,实现 xinfer-backend 中已有的 Device 与 Buffer 抽象,并补齐 kernel dispatch / executor 边界。Vulkan 概念可对应为:VkDevice/VkQueue ↔ Device,VkBuffer + VkDeviceMemory ↔ Buffer,compute pipeline + descriptor set ↔ Kernel/绑定,VkCommandBuffer + VkFence ↔ Executor。可复用的是上层模型、runtime、loader 与 tokenizer;需要替换的是 backend 实现和 shader 编译目标,例如把 HLSL 路径迁移到 SPIR-V 或重写 shader。