Skip to main content

oxirs_vec/gpu/
accelerator.rs

1//! Main GPU accelerator implementation
2
3use super::{GpuBuffer, GpuConfig, GpuDevice, GpuPerformanceStats, KernelManager};
4use crate::similarity::SimilarityMetric;
5use anyhow::{anyhow, Result};
6use parking_lot::RwLock;
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9
10/// CUDA stream handle
11#[derive(Debug)]
12pub struct CudaStream {
13    handle: *mut std::ffi::c_void,
14    device_id: i32,
15}
16
17unsafe impl Send for CudaStream {}
18unsafe impl Sync for CudaStream {}
19
20/// CUDA kernel handle
21#[derive(Debug)]
22pub struct CudaKernel {
23    function: *mut std::ffi::c_void,
24    module: *mut std::ffi::c_void,
25    name: String,
26}
27
28unsafe impl Send for CudaKernel {}
29unsafe impl Sync for CudaKernel {}
30
31/// Parameters for similarity kernel execution
32#[derive(Debug, Clone)]
33pub struct SimilarityKernelParams {
34    pub query_count: usize,
35    pub db_count: usize,
36    pub dim: usize,
37    pub metric: String,
38}
39
40/// GPU acceleration engine for vector operations
41#[derive(Debug)]
42pub struct GpuAccelerator {
43    config: GpuConfig,
44    device: GpuDevice,
45    memory_pool: Arc<Mutex<Vec<GpuBuffer>>>,
46    stream_pool: Vec<CudaStream>,
47    kernel_cache: Arc<RwLock<HashMap<String, CudaKernel>>>,
48    performance_stats: Arc<RwLock<GpuPerformanceStats>>,
49    kernel_manager: KernelManager,
50}
51
52unsafe impl Send for GpuAccelerator {}
53unsafe impl Sync for GpuAccelerator {}
54
55impl GpuAccelerator {
56    pub fn new(config: GpuConfig) -> Result<Self> {
57        config.validate()?;
58
59        let device = GpuDevice::get_device_info(config.device_id)?;
60        let memory_pool = Arc::new(Mutex::new(Vec::new()));
61        let stream_pool = Self::create_streams(config.stream_count, config.device_id)?;
62        let kernel_manager = KernelManager::new();
63
64        Ok(Self {
65            config,
66            device,
67            memory_pool,
68            stream_pool,
69            kernel_cache: Arc::new(RwLock::new(HashMap::new())),
70            performance_stats: Arc::new(RwLock::new(GpuPerformanceStats::new())),
71            kernel_manager,
72        })
73    }
74
75    fn create_streams(count: usize, device_id: i32) -> Result<Vec<CudaStream>> {
76        let mut streams = Vec::new();
77
78        for _ in 0..count {
79            let handle = Self::create_cuda_stream(device_id)?;
80            streams.push(CudaStream { handle, device_id });
81        }
82
83        Ok(streams)
84    }
85
86    #[allow(unused_variables)]
87    fn create_cuda_stream(device_id: i32) -> Result<*mut std::ffi::c_void> {
88        // Pure Rust build: placeholder handle. Real CUDA streams are created by
89        // oxirs-vec-adapter-cuda.
90        Ok(1 as *mut std::ffi::c_void)
91    }
92
93    /// Compute similarity between query vectors and database vectors
94    pub fn compute_similarity(
95        &self,
96        queries: &[f32],
97        database: &[f32],
98        query_count: usize,
99        db_count: usize,
100        dim: usize,
101        metric: SimilarityMetric,
102    ) -> Result<Vec<f32>> {
103        let timer = super::performance::GpuTimer::start("similarity_computation");
104
105        // Allocate GPU buffers
106        let mut query_buffer = GpuBuffer::new(queries.len(), self.config.device_id)?;
107        let mut db_buffer = GpuBuffer::new(database.len(), self.config.device_id)?;
108        let result_buffer = GpuBuffer::new(query_count * db_count, self.config.device_id)?;
109
110        // Copy data to GPU
111        query_buffer.copy_from_host(queries)?;
112        db_buffer.copy_from_host(database)?;
113
114        // Select appropriate kernel
115        let kernel_name = match metric {
116            SimilarityMetric::Cosine => "cosine_similarity",
117            SimilarityMetric::Euclidean => "euclidean_distance",
118            _ => return Err(anyhow!("Unsupported similarity metric for GPU")),
119        };
120
121        // Create kernel parameters
122        let params = SimilarityKernelParams {
123            query_count,
124            db_count,
125            dim,
126            metric: kernel_name.to_string(),
127        };
128
129        // Launch kernel
130        self.launch_similarity_kernel(
131            kernel_name,
132            &query_buffer,
133            &db_buffer,
134            &result_buffer,
135            &params,
136        )?;
137
138        // Copy results back
139        let mut results = vec![0.0f32; query_count * db_count];
140        result_buffer.copy_to_host(&mut results)?;
141
142        // Record performance
143        let duration = timer.stop();
144        self.performance_stats
145            .write()
146            .record_compute_operation(duration);
147
148        Ok(results)
149    }
150
151    fn launch_similarity_kernel(
152        &self,
153        kernel_name: &str,
154        query_buffer: &GpuBuffer,
155        db_buffer: &GpuBuffer,
156        result_buffer: &GpuBuffer,
157        params: &SimilarityKernelParams,
158    ) -> Result<()> {
159        // Pure Rust build computes on CPU. The CUDA kernel launch is provided by
160        // oxirs-vec-adapter-cuda.
161        self.compute_similarity_cpu(query_buffer, db_buffer, result_buffer, params, kernel_name)
162    }
163
164    fn compute_similarity_cpu(
165        &self,
166        _query_buffer: &GpuBuffer,
167        _db_buffer: &GpuBuffer,
168        _result_buffer: &GpuBuffer,
169        params: &SimilarityKernelParams,
170        _metric: &str,
171    ) -> Result<()> {
172        // Simplified CPU fallback
173        let query_data = vec![0.0f32; params.query_count * params.dim];
174        let db_data = vec![0.0f32; params.db_count * params.dim];
175        let mut results = vec![0.0f32; params.query_count * params.db_count];
176
177        // Copy data from "GPU" buffers (actually host memory in fallback)
178        // In real implementation, this would be proper GPU memory access
179
180        for i in 0..params.query_count {
181            for j in 0..params.db_count {
182                let query_vec = &query_data[i * params.dim..(i + 1) * params.dim];
183                let db_vec = &db_data[j * params.dim..(j + 1) * params.dim];
184
185                let similarity = match params.metric.as_str() {
186                    "cosine_similarity" => self.compute_cosine_similarity(query_vec, db_vec),
187                    "euclidean_distance" => self.compute_euclidean_distance(query_vec, db_vec),
188                    _ => 0.0,
189                };
190
191                results[i * params.db_count + j] = similarity;
192            }
193        }
194
195        Ok(())
196    }
197
198    fn compute_cosine_similarity(&self, a: &[f32], b: &[f32]) -> f32 {
199        let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
200        let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
201        let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
202
203        if norm_a > 1e-8 && norm_b > 1e-8 {
204            dot / (norm_a * norm_b)
205        } else {
206            0.0
207        }
208    }
209
210    fn compute_euclidean_distance(&self, a: &[f32], b: &[f32]) -> f32 {
211        a.iter()
212            .zip(b.iter())
213            .map(|(x, y)| (x - y).powi(2))
214            .sum::<f32>()
215            .sqrt()
216    }
217
218    fn get_or_compile_kernel(&self, name: &str) -> Result<CudaKernel> {
219        // Check if kernel is already compiled
220        if let Some(kernel) = self.kernel_cache.read().get(name) {
221            return Ok(CudaKernel {
222                function: kernel.function,
223                module: kernel.module,
224                name: kernel.name.clone(),
225            });
226        }
227
228        // Compile kernel
229        let kernel_source = self
230            .kernel_manager
231            .get_kernel(name)
232            .ok_or_else(|| anyhow!("Kernel {} not found", name))?;
233
234        let compiled_kernel = self.compile_kernel(name, kernel_source)?;
235
236        // Cache the compiled kernel
237        self.kernel_cache.write().insert(
238            name.to_string(),
239            CudaKernel {
240                function: compiled_kernel.function,
241                module: compiled_kernel.module,
242                name: compiled_kernel.name.clone(),
243            },
244        );
245
246        Ok(compiled_kernel)
247    }
248
249    fn compile_kernel(&self, name: &str, _source: &str) -> Result<CudaKernel> {
250        // Kernel-compilation bookkeeping (Pure Rust). Real NVRTC compilation is
251        // provided by oxirs-vec-adapter-cuda.
252        Ok(CudaKernel {
253            function: std::ptr::null_mut(),
254            module: std::ptr::null_mut(),
255            name: name.to_string(),
256        })
257    }
258
259    /// Get device information
260    pub fn device(&self) -> &GpuDevice {
261        &self.device
262    }
263
264    /// Get configuration
265    pub fn config(&self) -> &GpuConfig {
266        &self.config
267    }
268
269    /// Get performance statistics
270    pub fn performance_stats(&self) -> Arc<RwLock<GpuPerformanceStats>> {
271        self.performance_stats.clone()
272    }
273
274    /// Synchronize all operations
275    pub fn synchronize(&self) -> Result<()> {
276        // No-op in the Pure Rust build; real device sync is in oxirs-vec-adapter-cuda.
277        Ok(())
278    }
279
280    /// Reset performance statistics
281    pub fn reset_stats(&self) {
282        self.performance_stats.write().reset();
283    }
284
285    /// Get current GPU memory usage in bytes
286    pub fn get_memory_usage(&self) -> Result<usize> {
287        // Pure Rust build reports zero; real CUDA memory usage is in oxirs-vec-adapter-cuda.
288        Ok(0)
289    }
290}
291
292/// Check if GPU acceleration is available
293pub fn is_gpu_available() -> bool {
294    // Pure Rust build: no CUDA devices. Use oxirs-vec-adapter-cuda for real detection.
295    false
296}
297
298/// Create a default GPU accelerator configuration
299pub fn create_default_accelerator() -> Result<GpuAccelerator> {
300    let config = GpuConfig::default();
301    GpuAccelerator::new(config)
302}
303
304/// Create a performance-optimized GPU accelerator
305pub fn create_performance_accelerator() -> Result<GpuAccelerator> {
306    let config = GpuConfig {
307        optimization_level: crate::gpu::OptimizationLevel::Performance,
308        precision_mode: crate::gpu::PrecisionMode::FP32,
309        memory_pool_size: 1024 * 1024 * 1024, // 1GB
310        batch_size: 10000,
311        enable_tensor_cores: true,
312        enable_mixed_precision: false,
313        ..Default::default()
314    };
315    GpuAccelerator::new(config)
316}
317
318/// Create a memory-optimized GPU accelerator
319pub fn create_memory_optimized_accelerator() -> Result<GpuAccelerator> {
320    let config = GpuConfig {
321        optimization_level: crate::gpu::OptimizationLevel::Balanced,
322        precision_mode: crate::gpu::PrecisionMode::FP16,
323        memory_pool_size: 256 * 1024 * 1024, // 256MB
324        batch_size: 1000,
325        enable_tensor_cores: true,
326        enable_mixed_precision: true,
327        ..Default::default()
328    };
329    GpuAccelerator::new(config)
330}