Rust运行时插件库cubecl-runtime的使用,cubecl-runtime提供高性能运行时扩展与模块化支持
以下是关于Rust运行时插件库cubecl-runtime的使用内容:
CubeCL是一个多平台高性能计算语言扩展库,为Rust提供运行时扩展与模块化支持。它允许您使用Rust编写GPU计算程序,利用零成本抽象来开发可维护、灵活且高效的计算内核。
支持平台
平台 | 运行时 | 编译器 | 硬件 |
---|---|---|---|
WebGPU | wgpu | WGSL | 大多数GPU |
CUDA | CUDA | C++ (CUDA) | NVIDIA GPU |
ROCm | HIP | C++ (HIP) | AMD GPU |
Metal | wgpu | C++ (Metal) | Apple GPU |
Vulkan | wgpu | SPIR-V | Linux/Windows上的大多数GPU |
示例代码
内容中提供的示例展示了如何使用CubeCL编写GELU激活函数:
use cubecl::prelude::*;
#[cube(launch_unchecked)]
/// 一个[Line]代表可以执行SIMD操作的连续元素序列
/// 运行时会在可能时自动使用SIMD指令以提升性能
fn gelu_array<F: Float>(input: &Array<Line<F>>, output: &mut Array<Line<F>>) {
if ABSOLUTE_POS < input.len() {
output[ABSLUTE_POS] = gelu_scalar(input[ABSOLUTE_POS]);
}
}
#[cube]
fn gelu_scalar<F: Float>(x: Line<F>) -> Line<F> {
// 在编译时执行sqrt函数
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::<f32>(&input_handle, input.len(), vectorization as u8),
ArrayArg::from_raw_parts::<f32>(&output_handle, input.len(), vectorization as u8),
)
};
let bytes = client.read_one(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运行时
完整示例Demo
以下是一个完整的示例,展示如何使用cubecl-runtime:
use cubecl::prelude::*;
#[cube(launch_unchecked)]
fn vector_add<F: Float>(a: &Array<Line<F>>, b: &Array<Line<F>>, c: &mut Array<Line<F>>) {
if ABSOLUTE_POS < a.len() {
c[ABSOLUTE_POS] = a[ABSOLUTE_POS] + b[ABSOLUTE_POS];
}
}
fn main() {
// 初始化运行时
let device = cubecl::wgpu::Device::default();
// 创建客户端
let client = cubecl::wgpu::client(&device);
// 准备输入数据
let a_data = vec![1.0f32, 2.0, 3.0, 4.0];
let b_data = vec![5.0f32, 6.0, 7.0, 8.0];
// 创建GPU缓冲区
let a_handle = client.create(f32::as_bytes(&a_data));
let b_handle = client.create(f32::as_bytes(&b_data));
let c_handle = client.empty(a_data.len() * std::mem::size_of::<f32>());
// 启动内核
unsafe {
vector_add::launch_unchecked::<f32, cubecl::wgpu>(
&client,
CubeCount::Static(1, 1, 1),
CubeDim::new(a_data.len() as u32, 1, 1),
ArrayArg::from_raw_parts::<f32>(&a_handle, a_data.len(), 1),
ArrayArg::from_raw_parts::<f32>(&b_handle, b_data.len(), 1),
ArrayArg::from_raw_parts::<f32>(&c_handle, a_data.len(), 1),
);
}
// 读取结果
let result_bytes = client.read_one(c_handle.binding());
let result = f32::from_bytes(&result_bytes);
println!("Result: {:?}", result); // 应该输出 [6.0, 8.0, 10.0, 12.0]
}
工作原理
CubeCL利用Rust的proc宏系统进行两阶段处理:
- 解析:proc宏使用syn crate解析GPU内核代码
- 扩展:宏生成一个新的Rust函数,该函数在调用时创建中间表示(IR)
设计
CubeCL围绕立方体(Cubes)设计,使用3D表示来映射硬件架构:
特殊功能
- 自动向量化:可以指定输入变量的向量化因子,运行时能编译内核并使用最佳指令
- 编译时计算(Comptime):允许在运行时首次编译内核时修改编译器IR
- 自动调优:运行时运行小型基准测试以确定最佳内核配置
CubeCL目前处于alpha阶段,是Burn深度学习框架的一部分。
1 回复
cubecl-runtime: Rust高性能运行时插件库
介绍
cubecl-runtime 是一个为Rust程序提供高性能运行时扩展和模块化支持的库。它允许开发者在运行时动态加载和卸载功能模块,而无需重新编译主程序,非常适合需要插件系统或热更新功能的应用程序。
主要特性:
- 高性能的模块加载和执行
- 安全的模块隔离机制
- 简单的API设计
- 支持跨平台动态链接库加载
- 模块间通信支持
完整示例Demo
1. 创建插件项目
首先创建一个插件项目,结构如下:
// 插件项目的Cargo.toml
[package]
name = "my-plugin"
version = "0.1.0"
[lib]
crate-type = ["cdylib"] # 必须设置为cdylib才能作为动态库使用
[dependencies]
cubecl-runtime = "0.1"
// src/lib.rs
use cubecl_runtime::export_module;
#[export_module]
pub mod plugin {
pub fn square(x: i32) -> i32 {
x * x
}
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
pub fn sum_points(p1: Point, p2: Point) -> Point {
Point {
x: p1.x + p2.x,
y: p1.y + p2.y
}
}
}
// 共享数据结构
#[repr(C)]
pub struct Point {
pub x: f64,
pub y: f64,
}
2. 创建主程序项目
// 主程序的Cargo.toml
[package]
name = "main-app"
version = "0.1.0"
[dependencies]
cubecl-runtime = "0.1"
my-plugin = { path = "../my-plugin" } # 指向插件项目路径
// src/main.rs
use cubecl_runtime::{Runtime, Module};
use my_plugin::Point; // 使用共享数据结构
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建运行时环境
let mut runtime = Runtime::new();
// 加载插件模块
#[cfg(target_os = "windows")]
let module_path = "target/debug/my_plugin.dll";
#[cfg(target_os = "linux")]
let module_path = "target/debug/libmy_plugin.so";
#[cfg(target_os = "macos")]
let module_path = "target/debug/libmy_plugin.dylib";
let module = runtime.load_module(module_path)?;
// 调用平方函数
if let Some(square) = module.get_function::<fn(i32) -> i32>("square")? {
println!("5的平方是: {}", square(5)); // 输出: 25
}
// 调用问候函数
if let Some(greet) = module.get_function::<fn(&str) -> String>("greet")? {
let message = greet("World");
println!("{}", message); // 输出: Hello, World!
}
// 使用共享数据结构
if let Some(sum_points) = module.get_function::<fn(Point, Point) -> Point>("sum_points")? {
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = Point { x: 3.0, y: 4.0 };
let result = sum_points(p1, p2);
println!("点相加结果: ({}, {})", result.x, result.y); // 输出: (4.0, 6.0)
}
// 显式卸载模块(可选)
runtime.unload_module(module);
Ok(())
}
3. 构建和运行步骤
- 先构建插件:
cd my-plugin
cargo build
- 然后构建并运行主程序:
cd main-app
cargo run
注意事项
- 确保主程序和插件使用相同的Rust版本编译
- 跨平台时注意动态库的后缀(.dll/.so/.dylib)
- 复杂的泛型类型可能无法在模块边界使用
- 插件和主程序需要共享的数据结构要使用#[repr©]标记
- 实际项目中要考虑错误处理和模块生命周期管理
这个完整示例展示了如何使用cubecl-runtime创建插件系统,包括函数调用、字符串处理和共享数据结构的使用。