Rust CUDA加速计算库ug-cuda的使用,高性能GPU并行计算与CUDA编程支持
Rust CUDA加速计算库ug-cuda的使用
ug-cuda是一个Rust库,提供了高性能GPU并行计算与CUDA编程支持。它允许Rust开发者利用NVIDIA GPU的强大计算能力进行加速计算。
安装
在您的项目目录中运行以下Cargo命令:
cargo add ug-cuda
或者在Cargo.toml中添加以下行:
ug-cuda = "0.4.0"
示例代码
以下是一个使用ug-cuda进行GPU计算的完整示例:
use ug_cuda::{CudaDevice, CudaSlice, LaunchAsync};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化CUDA设备
let device = CudaDevice::new(0)?;
// 创建输入数据
let host_input = vec![1.0f32, 2.0, 3.0, 4.0];
// 将数据复制到GPU
let dev_input = device.htod_sync_copy(&host_input)?;
// 创建GPU输出缓冲区
let mut dev_output = device.alloc_zeros::<f32>(host_input.len())?;
// 定义CUDA内核(使用PTX汇编)
let ptx = r#"
.version 6.5
.target sm_30
.address_size 64
.visible .entry kernel(
.param .u64 ptr_in,
.param .u64 ptr_out,
.param .u32 len
) {
.reg .f32 %f<4>;
.reg .pred %p<2>;
.reg .u32 %r<6>;
.reg .u64 %rl<4>;
ld.param.u64 %rl0, [ptr_in];
ld.param.u64 %rl1, [ptr_out];
ld.param.u32 %r0, [len];
mov.u32 %r1, %tid.x;
mov.u32 %r2, %ctaid.x;
mov.u32 %r3, %ntid.x;
mad.lo.s32 %r4, %r2, %r3, %r1;
setp.ge.u32 %p0, %r4, %r0;
@%p0 bra BB0_2;
cvta.to.global.u64 %rl2, %rl0;
cvta.to.global.u64 %rl3, %rl1;
mul.wide.u32 %rl4, %r4, 4;
add.s64 %rl5, %rl2, %rl4;
ld.global.f32 %f1, [%rl5];
add.s64 %rl6, %rl3, %rl4;
mul.f32 %f2, %f1, %f1;
st.global.f32 [%rl6], %f2;
BB0_2:
ret;
}
"#;
// 加载内核
let module = device.load极速计算示例:
```rust
use ug_cuda::{CudaDevice, CudaSlice, LaunchAsync, LaunchConfig};
// 向量平方计算内核
const SQUARE_KERNEL: &str = r#"
.version 6.5
.target sm_30
.address_size 64
.visible .entry square_kernel(
.param .u64 input_ptr,
.param .u64 output_ptr,
.param .u32 length
) {
.reg .f32 %f<4>;
.reg .pred %p<2>;
.reg .u32 %r<6>;
.reg .u64 %rl<4>;
ld.param.u64 %rl0, [input_ptr];
ld.param.u64 %rl1, [output_ptr];
ld.param.u32 %r0, [length];
// 计算线程索引
mov.u32 %r1, %tid.x;
mov.u32 %r2, %ctaid.x;
mov.u32 %r3, %ntid.x;
mad.lo.s32 %r4, %r2, %r3, %r1;
// 边界检查
setp.ge.u32 %p0, %r4, %r0;
@%p0 bra EXIT;
// 计算地址并加载数据
cvta.to.global.u64 %rl2, %rl0;
cvta.to.global.u64 %rl3, %rl1;
mul.wide.u32 %rl4, %r4, 4;
add.s64 %rl5, %rl2, %rl4;
ld.global.f32 %f1, [%rl5];
// 计算平方并存储
add.s64 %rl6, %rl3, %rl4;
mul.f32 %f2, %f1, %f1;
st.global.f32 [%rl6], %f2;
EXIT:
ret;
}
"#;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化CUDA设备(使用第一个GPU)
let device = CudaDevice::new(0)?;
// 准备测试数据: 1到10的浮点数
let host_input: Vec<f32> = (1..=10).map(|x| x as f32).collect();
// 将数据复制到GPU显存
let dev_input = device.htod_sync_copy(&host_input)?;
// 分配GPU输出缓冲区(初始化为0)
let mut dev_output = device.alloc_zeros::<f32>(host_input.len())?;
// 加载PTX内核
let module = device.load_ptx(SQUARE_KERNEL.into(), "square_module", "square_kernel")?;
let kernel = module.function("square_kernel")?;
// 配置内核启动参数
let blocks = (host_input.len() as u32 + 255) / 256; // 每个块256个线程
let config = LaunchConfig {
grid_dim: (blocks, 1, 1),
block_dim: (256, 1, 1),
shared_mem_bytes: 0,
};
// 异步启动内核
unsafe {
LaunchAsync::launch(
&kernel,
config,
(&dev_input, &mut dev_output, host_input.len() as u32),
)?
}
// 同步等待计算完成
device.synchronize()?;
// 将结果从GPU复制回CPU
let mut host_output = vec![0.0f32; host_input.len()];
device.dtoh_sync_copy_into(&dev_output, &mut host_output)?;
// 打印输入输出对比
println!("输入数据: {:?}", host_input);
println!("平方结果: {:?}", host_output);
Ok(())
}
功能特性
- CUDA设备管理:轻松初始化和管理CUDA设备
- 内存管理:在主机和设备间高效传输数据
- 内核执行:支持加载和执行CUDA PTX内核
- 异步操作:提供异步操作支持以提高性能
- 错误处理:全面的错误处理机制
许可证
ug-cuda采用MIT或Apache-2.0双重许可证。
1 回复
Rust CUDA加速计算库ug-cuda使用指南
概述
ug-cuda是一个Rust库,提供了对CUDA加速计算的支持,让开发者能够在Rust中利用GPU进行高性能并行计算。它封装了CUDA的底层API,提供了更符合Rust习惯的安全接口。
主要特性
- 安全的CUDA内存管理
- 设备管理接口
- 内核函数启动
- 流和事件管理
- 与Rust生态系统的良好集成
安装方法
在Cargo.toml中添加依赖:
[dependencies]
ug-cuda = "0.1"
需要安装CUDA工具包(至少CUDA 10.0)和兼容的NVIDIA显卡驱动。
完整示例代码
1. 初始化CUDA环境
use ug_cuda::driver::{initialize, Device};
fn main() {
// 初始化CUDA驱动
initialize().unwrap();
// 获取设备数量
let device_count = Device::count().unwrap();
println!("找到 {} 个CUDA设备", device_count);
// 获取第一个设备
let device = Device::get(0).unwrap();
println!("使用设备: {}", device.name().unwrap());
}
2. 内存管理示例
use ug_cuda::memory::{DeviceBox, DeviceCopy};
fn main() {
// 在设备上分配内存
let mut dev_vec = DeviceBox::new(&[1.0f32, 2.0, 3.0]).unwrap();
// 从设备内存复制回主机
let mut host_data = [0.0f32; 3];
dev_vec.copy_to(&mut host_data).unwrap();
println!("从设备获取的数据: {:?}", host_data);
}
3. 完整向量加法示例
3.1 编写CUDA内核(kernel.cu)
extern "C" __global__ void add_vectors(const float* a, const float* b, float* c, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
c[i] = a[i] + b[i];
}
}
需要先编译为PTX文件:
nvcc --ptx -o kernel.ptx kernel.cu
3.2 Rust主程序
use ug_cuda::{driver::LaunchAsync, module::Module, memory::DeviceBox};
fn main() {
// 初始化CUDA环境
ug_cuda::driver::initialize().unwrap();
// 加载编译好的CUDA模块
let module = match Module::from_file("kernel.ptx") {
Ok(m) => m,
Err(e) => {
eprintln!("无法加载PTX文件: {}", e);
return;
}
};
// 获取内核函数
let kernel = match module.function("add_vectors") {
Ok(k) => k,
Err(e) => {
eprintln!("找不到内核函数: {}", e);
return;
}
};
// 准备数据
let a = [1.0f32, 2.0, 3.0];
let b = [4.0f32, 5.0, 6.0];
let mut c = [0.0f32; 3];
// 分配设备内存
let d_a = DeviceBox::new(&a).unwrap();
let d_b = DeviceBox::new(&b).unwrap();
let mut d_c = DeviceBox::new(&c).unwrap();
// 启动内核
unsafe {
if let Err(e) = kernel.launch(
&[(&d_a, &d_b, &mut d_c, &3)],
1, // gridDim
3, // blockDim
0, // sharedMemBytes
None // stream
) {
eprintln!("内核执行失败: {}", e);
return;
}
}
// 复制结果回主机
if let Err(e) = d_c.copy_to(&mut c) {
eprintln!("数据复制失败: {}", e);
return;
}
println!("计算结果: {:?}", c); // 输出: [5.0, 7.0, 9.0]
}
4. 异步操作完整示例
use ug_cuda::{driver::{Stream, initialize}, memory::{DeviceBox, DeviceCopy}};
fn main() {
// 初始化CUDA
initialize().unwrap();
// 创建CUDA流
let stream = Stream::new().unwrap();
// 准备数据
let host_data = [1u32, 2, 3, 4];
let mut dev_data = DeviceBox::new_uninit().unwrap();
// 异步内存复制
if let Err(e) = dev_data.copy_from_async(&host_data, &stream) {
eprintln!("异步复制失败: {}", e);
return;
}
// 等待流完成
if let Err(e) = stream.synchronize() {
eprintln!("流同步失败: {}", e);
return;
}
println!("异步操作完成");
}
5. 性能计时完整示例
use ug_cuda::driver::{Event, initialize};
fn main() {
// 初始化CUDA
initialize().unwrap();
// 创建事件
let start = Event::new().unwrap();
let stop = Event::new().unwrap();
// 记录开始时间
if let Err(e) = start.record(None) {
eprintln!("无法记录开始事件: {}", e);
return;
}
// 模拟需要计时的操作
for _ in 0..1000 {
// 一些计算...
}
// 记录结束时间
if let Err(e) = stop.record(None) {
eprintln!("无法记录结束事件: {}", e);
return;
}
// 等待事件完成
if let Err(e) = stop.synchronize() {
eprintln!("事件同步失败: {}", e);
return;
}
// 计算耗时
match start.elapsed(&stop) {
Ok(time) => println!("操作耗时: {} 毫秒", time),
Err(e) => eprintln!("无法计算耗时: {}", e),
}
}
性能提示
- 尽量减少主机与设备间的数据传输
- 使用合适的block和grid大小
- 利用共享内存减少全局内存访问
- 使用异步操作和流来重叠计算与数据传输
注意事项
- ug-cuda仍在活跃开发中,API可能会有变动
- 需要正确安装CUDA工具链
- 内核代码需要单独编译为PTX文件
- 错误处理很重要,CUDA操作可能会失败
通过ug-cuda,Rust开发者可以安全高效地利用GPU的计算能力,同时享受Rust的内存安全保证。