Skip to main content

pagesift_gpu/
buffer.rs

1//! 显存与内核启动的安全封装:`DeviceBuffer`(f32 数组,离开作用域时释放)、内核参数打包、
2//! 用 CUDA 事件计时的 `Timer`。
3
4use std::ffi::c_void;
5use std::ptr::null_mut;
6
7use crate::Gpu;
8use crate::driver::{DevPtr, EVENT_DEFAULT, Handle};
9
10/// 显存里的一段 f32。借用创建它的 [`Gpu`],保证在上下文释放前先释放。
11pub struct DeviceBuffer<'g> {
12    gpu: &'g Gpu,
13    ptr: DevPtr,
14    len: usize,
15}
16
17impl Gpu {
18    /// 分配 `len` 个 f32 的显存(内容未初始化)。`len` 为 0 时不调驱动。
19    pub fn alloc(&self, len: usize) -> Result<DeviceBuffer<'_>, String> {
20        self.bind()?;
21        let mut ptr = 0;
22        if len > 0 {
23            let bytes = len
24                .checked_mul(size_of::<f32>())
25                .ok_or("GPU buffer size overflow")?;
26            self.drv.check(
27                unsafe { (self.drv.mem_alloc)(&mut ptr, bytes) },
28                "cuMemAlloc",
29            )?;
30        }
31        Ok(DeviceBuffer {
32            gpu: self,
33            ptr,
34            len,
35        })
36    }
37
38    /// 分配显存并拷入 `data`。
39    pub fn upload(&self, data: &[f32]) -> Result<DeviceBuffer<'_>, String> {
40        let mut buf = self.alloc(data.len())?;
41        buf.copy_from_host(data)?;
42        Ok(buf)
43    }
44}
45
46impl<'g> DeviceBuffer<'g> {
47    /// 元素个数。
48    pub fn len(&self) -> usize {
49        self.len
50    }
51
52    pub fn is_empty(&self) -> bool {
53        self.len == 0
54    }
55
56    /// 设备地址(传给内核用)。
57    pub(crate) fn ptr(&self) -> DevPtr {
58        self.ptr
59    }
60
61    /// 从内存拷入,长度必须相等。
62    pub fn copy_from_host(&mut self, data: &[f32]) -> Result<(), String> {
63        assert_eq!(data.len(), self.len, "拷入长度与显存长度不同");
64        if self.len == 0 {
65            return Ok(());
66        }
67        let g = self.gpu;
68        g.bind()?;
69        let r = unsafe {
70            (g.drv.memcpy_htod)(
71                self.ptr,
72                data.as_ptr() as *const c_void,
73                self.len * size_of::<f32>(),
74            )
75        };
76        g.drv.check(r, "cuMemcpyHtoD")
77    }
78
79    /// 拷回内存,长度必须相等。默认流上同步执行:之前提交的内核先跑完。
80    pub fn copy_to_host(&self, out: &mut [f32]) -> Result<(), String> {
81        assert_eq!(out.len(), self.len, "拷回长度与显存长度不同");
82        if self.len == 0 {
83            return Ok(());
84        }
85        let g = self.gpu;
86        g.bind()?;
87        let r = unsafe {
88            (g.drv.memcpy_dtoh)(
89                out.as_mut_ptr() as *mut c_void,
90                self.ptr,
91                self.len * size_of::<f32>(),
92            )
93        };
94        g.drv.check(r, "cuMemcpyDtoH")
95    }
96
97    /// 把 `data` 拷到开头(`data` 可以比缓冲短,缓冲池给的容量常常多一点)。
98    pub(crate) fn write_prefix(&self, data: &[f32]) -> Result<(), String> {
99        assert!(data.len() <= self.len, "拷入长度超过显存长度");
100        if data.is_empty() {
101            return Ok(());
102        }
103        let g = self.gpu;
104        g.bind()?;
105        let r = unsafe {
106            (g.drv.memcpy_htod)(
107                self.ptr,
108                data.as_ptr() as *const c_void,
109                std::mem::size_of_val(data),
110            )
111        };
112        g.drv.check(r, "cuMemcpyHtoD")
113    }
114
115    /// 从开头拷回 `out.len()` 个元素。默认流上同步执行:之前提交的内核先跑完。
116    pub(crate) fn read_prefix(&self, out: &mut [f32]) -> Result<(), String> {
117        assert!(out.len() <= self.len, "拷回长度超过显存长度");
118        if out.is_empty() {
119            return Ok(());
120        }
121        let g = self.gpu;
122        g.bind()?;
123        let r = unsafe {
124            (g.drv.memcpy_dtoh)(
125                out.as_mut_ptr() as *mut c_void,
126                self.ptr,
127                std::mem::size_of_val(out),
128            )
129        };
130        g.drv.check(r, "cuMemcpyDtoH")
131    }
132
133    /// 拷回成新的 `Vec`。
134    pub fn to_vec(&self) -> Result<Vec<f32>, String> {
135        let mut v = vec![0f32; self.len];
136        self.copy_to_host(&mut v)?;
137        Ok(v)
138    }
139
140    /// 从另一段显存拷入,长度必须相等。
141    pub fn copy_from_device(&mut self, src: &DeviceBuffer<'_>) -> Result<(), String> {
142        assert_eq!(src.len, self.len, "显存拷贝长度不同");
143        if self.len == 0 {
144            return Ok(());
145        }
146        let g = self.gpu;
147        g.bind()?;
148        g.drv.check(
149            unsafe { (g.drv.memcpy_dtod)(self.ptr, src.ptr, self.len * size_of::<f32>()) },
150            "cuMemcpyDtoD",
151        )
152    }
153}
154
155impl Drop for DeviceBuffer<'_> {
156    fn drop(&mut self) {
157        if self.len > 0 && self.gpu.bind().is_ok() {
158            unsafe { (self.gpu.drv.mem_free)(self.ptr) };
159        }
160    }
161}
162
163/// 内核参数:每个参数放在自己的 8 字节槽里,`cuLaunchKernel` 按内核签名的宽度从槽首读取
164/// (小端机器上 u32 就在槽的低 4 字节)。
165#[derive(Default)]
166pub(crate) struct Args {
167    slots: Vec<u64>,
168}
169
170impl Args {
171    pub fn ptr(mut self, b: &DeviceBuffer<'_>) -> Self {
172        self.slots.push(b.ptr());
173        self
174    }
175
176    pub fn raw_ptr(mut self, p: DevPtr) -> Self {
177        self.slots.push(p);
178        self
179    }
180
181    /// u32 参数;超出 u32 的尺寸是调用方的错(显存放不下那么大的张量),这里直接断言。
182    pub fn u32(mut self, v: usize) -> Self {
183        self.slots
184            .push(u32::try_from(v).expect("内核参数超出 u32") as u64);
185        self
186    }
187
188    pub fn u64(mut self, v: usize) -> Self {
189        self.slots.push(v as u64);
190        self
191    }
192
193    pub fn f32(mut self, v: f32) -> Self {
194        self.slots.push(v.to_bits() as u64);
195        self
196    }
197}
198
199const _: () = assert!(cfg!(target_endian = "little"), "Args 的槽布局假定小端");
200
201impl Gpu {
202    /// 在默认流上启动内核(异步)。`grid`、`block` 是 (x, y, z)。
203    pub(crate) fn launch(
204        &self,
205        f: Handle,
206        grid: (u32, u32, u32),
207        block: (u32, u32, u32),
208        args: Args,
209    ) -> Result<(), String> {
210        if grid.0 == 0 || grid.1 == 0 || grid.2 == 0 {
211            return Ok(()); // 空张量:没有工作
212        }
213        self.bind()?;
214        let mut slots = args.slots;
215        let mut ptrs: Vec<*mut c_void> = slots
216            .iter_mut()
217            .map(|s| s as *mut u64 as *mut c_void)
218            .collect();
219        let r = unsafe {
220            (self.drv.launch_kernel)(
221                f,
222                grid.0,
223                grid.1,
224                grid.2,
225                block.0,
226                block.1,
227                block.2,
228                0,
229                null_mut(),
230                ptrs.as_mut_ptr(),
231                null_mut(),
232            )
233        };
234        self.drv.check(r, "cuLaunchKernel")
235    }
236}
237
238/// 用一对 CUDA 事件量设备上一段工作的耗时(只算 GPU 执行,不含主机侧开销)。
239pub struct Timer<'g> {
240    gpu: &'g Gpu,
241    start: Handle,
242    end: Handle,
243}
244
245impl Gpu {
246    /// 建计时器并记下起点。
247    pub fn timer(&self) -> Result<Timer<'_>, String> {
248        self.bind()?;
249        let mut t = Timer {
250            gpu: self,
251            start: null_mut(),
252            end: null_mut(),
253        };
254        let d = &self.drv;
255        d.check(
256            unsafe { (d.event_create)(&mut t.start, EVENT_DEFAULT) },
257            "cuEventCreate",
258        )?;
259        d.check(
260            unsafe { (d.event_create)(&mut t.end, EVENT_DEFAULT) },
261            "cuEventCreate",
262        )?;
263        d.check(
264            unsafe { (d.event_record)(t.start, null_mut()) },
265            "cuEventRecord",
266        )?;
267        Ok(t)
268    }
269}
270
271impl Timer<'_> {
272    /// 记下终点,等它完成,返回起点到终点的毫秒数。
273    pub fn stop_ms(&self) -> Result<f32, String> {
274        let d = &self.gpu.drv;
275        self.gpu.bind()?;
276        d.check(
277            unsafe { (d.event_record)(self.end, null_mut()) },
278            "cuEventRecord",
279        )?;
280        d.check(
281            unsafe { (d.event_synchronize)(self.end) },
282            "cuEventSynchronize",
283        )?;
284        let mut ms = 0f32;
285        d.check(
286            unsafe { (d.event_elapsed_time)(&mut ms, self.start, self.end) },
287            "cuEventElapsedTime",
288        )?;
289        Ok(ms)
290    }
291}
292
293impl Drop for Timer<'_> {
294    fn drop(&mut self) {
295        if self.gpu.bind().is_ok() {
296            for e in [self.start, self.end] {
297                if !e.is_null() {
298                    unsafe { (self.gpu.drv.event_destroy)(e) };
299                }
300            }
301        }
302    }
303}