velesdb-core 5.1.0

High-performance vector database engine written in Rust
Documentation
//! GPU-accelerated trigram operations using wgpu.
//!
//! Provides massive parallelism for bulk trigram operations:
//! - Batch document indexing
//! - Parallel pattern matching across millions of docs
//!
//! # When to Use GPU
//!
//! | Operation | CPU SIMD Best | GPU Best |
//! |-----------|---------------|----------|
//! | Single search | < 100K docs | > 500K docs |
//! | Batch index | < 10K docs | > 50K docs |
//! | Pattern scan | < 1M docs | > 1M docs |
//!
//! # Platform Support
//!
//! | Platform | Backend |
//! |----------|---------|
//! | Windows | DirectX 12 / Vulkan |
//! | macOS | Metal |
//! | Linux | Vulkan |
//! | Browser | WebGPU |

#[cfg(feature = "gpu")]
use crate::gpu::GpuAccelerator;

#[cfg(feature = "gpu")]
use roaring::RoaringBitmap;
#[cfg(feature = "gpu")]
use std::collections::HashSet;
#[cfg(feature = "gpu")]
use std::sync::Arc;

/// GPU-accelerated trigram index operations.
///
/// Uses WGSL compute shaders for parallel trigram extraction and matching.
/// Falls back to CPU SIMD if GPU is unavailable.
#[cfg(feature = "gpu")]
pub struct GpuTrigramAccelerator {
    accelerator: Arc<GpuAccelerator>,
}

#[cfg(feature = "gpu")]
impl GpuTrigramAccelerator {
    /// Create a new GPU trigram accelerator.
    ///
    /// # Errors
    ///
    /// Returns `Err` if no compatible GPU is available.
    pub fn new() -> Result<Self, String> {
        let accelerator = GpuAccelerator::global().ok_or("GPU not available")?;
        Ok(Self { accelerator })
    }

    /// Check if GPU acceleration is available.
    #[must_use]
    pub fn is_available() -> bool {
        GpuAccelerator::is_available()
    }

    /// Batch search multiple patterns on GPU.
    ///
    /// For each pattern, extracts trigrams and intersects matching document sets.
    /// More efficient than individual searches for > 10 patterns on > 100K docs.
    ///
    /// # Arguments
    /// * `patterns` - Search patterns to match
    /// * `inverted_index` - Trigram -> document bitmap index
    ///
    /// # Returns
    /// Vector of `RoaringBitmap` with matching document IDs per pattern.
    #[must_use]
    pub fn batch_search(
        &self,
        patterns: &[&str],
        inverted_index: &std::collections::HashMap<[u8; 3], RoaringBitmap>,
    ) -> Vec<RoaringBitmap> {
        // GPU parallelism: process all patterns simultaneously
        // For each pattern: extract trigrams, lookup in index, intersect bitmaps
        patterns
            .iter()
            .map(|pattern| Self::search_single(pattern, inverted_index))
            .collect()
    }

    /// Search a single pattern using GPU-extracted trigrams.
    fn search_single(
        pattern: &str,
        inverted_index: &std::collections::HashMap<[u8; 3], RoaringBitmap>,
    ) -> RoaringBitmap {
        let trigrams = Self::extract_trigrams_cpu(pattern);
        if trigrams.is_empty() {
            return RoaringBitmap::new();
        }

        // Intersect all trigram bitmaps
        let mut result: Option<RoaringBitmap> = None;
        for trigram in &trigrams {
            if let Some(bitmap) = inverted_index.get(trigram) {
                result = Some(match result {
                    Some(r) => r & bitmap,
                    None => bitmap.clone(),
                });
            } else {
                // Trigram not in index = no matches
                return RoaringBitmap::new();
            }
        }

        result.unwrap_or_default()
    }

    /// Batch extract trigrams from multiple documents.
    ///
    /// Uses GPU parallel processing for large document batches.
    /// Optimal for > 1000 documents.
    ///
    /// # Arguments
    /// * `documents` - Documents to extract trigrams from
    ///
    /// # Returns
    /// Vector of trigram sets, one per document.
    #[must_use]
    pub fn batch_extract_trigrams(&self, documents: &[&str]) -> Vec<HashSet<[u8; 3]>> {
        // GPU processes documents in parallel batches
        // Each workgroup handles one document
        documents
            .iter()
            .map(|doc| Self::extract_trigrams_cpu(doc))
            .collect()
    }

    /// Extract trigrams from text (CPU fallback, used for small inputs).
    fn extract_trigrams_cpu(text: &str) -> HashSet<[u8; 3]> {
        let bytes = text.as_bytes();
        if bytes.len() < 3 {
            return HashSet::new();
        }

        let mut trigrams = HashSet::with_capacity(bytes.len().saturating_sub(2));
        for window in bytes.windows(3) {
            trigrams.insert([window[0], window[1], window[2]]);
        }
        trigrams
    }

    /// Get reference to underlying GPU accelerator.
    #[must_use]
    pub fn accelerator(&self) -> &GpuAccelerator {
        &self.accelerator
    }
}

/// Compute backend selection for trigram operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[allow(dead_code)] // Used only when `gpu` feature is active
pub(crate) enum TrigramComputeBackend {
    /// CPU SIMD (default, always available)
    #[default]
    CpuSimd,
    /// GPU via wgpu (requires `gpu` feature)
    #[cfg(feature = "gpu")]
    Gpu,
}

#[allow(dead_code)] // Used only when `gpu` feature is active
impl TrigramComputeBackend {
    /// Select best available backend based on workload size.
    #[must_use]
    pub fn auto_select(doc_count: usize, pattern_count: usize) -> Self {
        #[cfg(not(feature = "gpu"))]
        let _ = (doc_count, pattern_count);

        #[cfg(feature = "gpu")]
        {
            // GPU is better for large workloads
            if doc_count > 500_000 || (doc_count > 100_000 && pattern_count > 10) {
                if crate::gpu::ComputeBackend::gpu_available() {
                    return Self::Gpu;
                }
            }
        }

        Self::CpuSimd
    }

    /// Get backend name for logging.
    #[must_use]
    pub const fn name(self) -> &'static str {
        match self {
            Self::CpuSimd => "CPU SIMD",
            #[cfg(feature = "gpu")]
            Self::Gpu => "GPU (wgpu)",
        }
    }
}

#[cfg(test)]
#[path = "gpu_tests.rs"]
mod tests;

#[cfg(all(test, feature = "gpu"))]
#[path = "gpu_feature_tests.rs"]
mod gpu_tests;