使用 CubeCL,您可以通过 Rust 编写 GPU 程序,借助零成本抽象开发 出可维护、灵活且高效的计算内核。CubeCL 目前完全支持函数、泛型和结构体,并部分支持 trait、方法和类型推导。随着项目的演进,我们预计将支持更多 Rust 语言特性,同时保持最佳性能。
示例
只需用 cube 属性标注函数,即可指定其运行在 GPU 上:
use cubecl::prelude::*;
#[cube(launch_unchecked)]
fn gelu_array<F: Float>(input: &Array<Line<F>>, output: &mut Array<Line<F>>) {
if ABSOLUTE_POS < input.len() {
output[ABSOLUTE_POS] = gelu_scalar(input[ABSOLUTE_POS]);
}
}
#[cube]
fn gelu_scalar<F: Float>(x: Line<F>) -> Line<F> {
let sqrt2 = F::new(comptime!(2.0f32.sqrt()));
let tmp = x / Line::new(sqrt2);
x * (Line::erf(tmp) + 1.0) / 2.0
}
通过自动生成的 gelu_array::launch_unchecked 函数即可启动内核:
pub fn launch<R: Runtime>(device: &R::Device) {
let client = R::client(device);
let input = &[-1., 0., 1., 5.];
let vectorization = 4;
let output_handle = client.empty(input.len() * core::mem::size_of::<f32>());
let input_handle = client.create(f32::as_bytes(input));
unsafe {
gelu_array::launch_unchecked::<f32, R>(
&client,
CubeCount::Static(1, 1, 1),
CubeDim::new(input.len() as u32 / vectorization, 1, 1),
ArrayArg::from_raw_parts(&input_handle, input.len(), vectorization as u8),
ArrayArg::from_raw_parts(&output_handle, input.len(), vectorization as u8),
)
};
let bytes = client.read(output_handle.binding());
let output = f32::from_bytes(&bytes);
println!("Executed gelu with runtime {:?} => {output:?}", R::name());
}
运行以下命令即可体验 GELU 示例:
cargo run --example gelu --features cuda # 使用 CUDA 运行时
cargo run --example gelu --features wgpu # 使用 WGPU 运行时
运行时支持
支持以下 GPU 运行时:
未来还计划开发一个使用 SIMD 指令的优化 JIT CPU 运行时, 基于 Cranelift。