T1: scaffold repo + Rust/CUDA build chain (vecadd smoke test)
Stand up the xtrain project skeleton: a Cargo workspace mirroring xserv's csrc/ + crates/ layout, with a single xtrain-cuda crate that wraps the CUDA Runtime over hand-written extern "C" FFI. build.rs compiles csrc/test/vecadd.cu via the cc crate targeting sm_120 (RTX 5090) and links cudart. A gated integration test runs the vector-add kernel on the GPU and asserts the result. When nvcc is absent (local GPU-less machine), build.rs skips CUDA compilation and sets a `no_cuda` cfg so host-side cargo check still works. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
59
crates/xtrain-cuda/src/memory.rs
Normal file
59
crates/xtrain-cuda/src/memory.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::error::{self, Result};
|
||||
use crate::ffi;
|
||||
|
||||
/// RAII wrapper around a GPU memory allocation. Dropping frees the memory.
|
||||
pub struct GpuBuffer {
|
||||
ptr: *mut u8,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl GpuBuffer {
|
||||
pub fn alloc(len: usize) -> Result<Self> {
|
||||
assert!(len > 0, "cannot allocate 0 bytes on GPU");
|
||||
let mut ptr = std::ptr::null_mut();
|
||||
error::check(unsafe { ffi::cudaMalloc(&mut ptr, len) })?;
|
||||
Ok(Self { ptr, len })
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Copy data from a host (CPU) slice to this GPU buffer.
|
||||
pub fn copy_from_host(&mut self, src: &[u8]) -> Result<()> {
|
||||
assert!(src.len() <= self.len, "source larger than buffer");
|
||||
error::check(unsafe {
|
||||
ffi::cudaMemcpy(self.ptr, src.as_ptr(), src.len(), ffi::CUDA_MEMCPY_H2D)
|
||||
})
|
||||
}
|
||||
|
||||
/// Copy data from this GPU buffer to a host (CPU) slice.
|
||||
pub fn copy_to_host(&self, dst: &mut [u8]) -> Result<()> {
|
||||
assert!(dst.len() <= self.len, "destination larger than buffer");
|
||||
error::check(unsafe {
|
||||
ffi::cudaMemcpy(dst.as_mut_ptr(), self.ptr, dst.len(), ffi::CUDA_MEMCPY_D2H)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GpuBuffer {
|
||||
fn drop(&mut self) {
|
||||
if !self.ptr.is_null() {
|
||||
unsafe { ffi::cudaFree(self.ptr) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for GpuBuffer {}
|
||||
Reference in New Issue
Block a user