Files
foxhunt/crates/ml-labeling/src/gpu_acceleration.rs
jgrusewski 7f43963d2f fix(clippy): ml-supervised + ml-core + thin crates — 34 errors fixed
ml-supervised: doc backticks, const fn, removed redundant clones,
  underscore-prefixed params that were actually used
ml-labeling: const fn on gpu_acceleration::new()
ml-core/ml-ensemble/ml-explainability: already clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:39:03 +01:00

163 lines
4.8 KiB
Rust

//! GPU acceleration for batch labeling operations
//!
//! Provides GPU acceleration (CUDA only) via cudarc integration for high-throughput labeling workloads.
use std::error::Error;
use std::fmt;
use ml_core::device::MlDevice;
use super::types::EventLabel;
/// `GPU`-accelerated labeling engine
#[derive(Debug)]
pub struct GPULabelingEngine {
device: MlDevice,
}
impl GPULabelingEngine {
/// Create new `GPU` labeling engine
pub const fn new(device: MlDevice) -> Result<Self, LabelingError> {
Ok(Self { device })
}
/// Check if `GPU` is available
pub fn gpu_available() -> bool {
#[cfg(feature = "cuda")]
{
MlDevice::cuda(0).is_ok()
}
#[cfg(not(feature = "cuda"))]
{
false
}
}
/// Get optimal batch size for `GPU` operations (CUDA mandatory)
pub const fn optimal_batch_size() -> usize {
4096 // GPU batch size — CUDA is mandatory
}
/// Get the device this engine is bound to
pub const fn device(&self) -> &MlDevice {
&self.device
}
/// Process batch of price data on `GPU`
pub fn process_batch(
&self,
prices: &[f64],
timestamps: &[u64],
) -> Result<Vec<EventLabel>, LabelingError> {
let batch_size = prices.len().min(timestamps.len());
let mut labels = Vec::new();
for i in 0..batch_size {
// Simplified label creation - in practice this would be GPU-accelerated
let label = EventLabel::new(
timestamps[i],
(prices[i] * 100.0) as u64, // Convert to cents
super::types::BarrierResult::TimeExpiry, // Use enum variant
0, // neutral label
0, // no return
0.5, // medium quality
10, // 10us processing
);
labels.push(label);
}
Ok(labels)
}
}
/// Labeling error types
#[derive(Debug, Clone, PartialEq)]
pub enum LabelingError {
/// Validation error
ValidationError(String),
/// Configuration error (alternate name)
ConfigError(String),
/// Computation error
ComputationError(String),
/// Configuration error
ConfigurationError(String),
/// `GPU` error
GpuError(String),
/// Invalid input error
InvalidInput(String),
}
impl fmt::Display for LabelingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelingError::ValidationError(msg) => write!(f, "Validation error: {}", msg),
LabelingError::ConfigError(msg) => write!(f, "Config error: {}", msg),
LabelingError::ComputationError(msg) => write!(f, "Computation error: {}", msg),
LabelingError::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
LabelingError::GpuError(msg) => write!(f, "GPU error: {}", msg),
LabelingError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
}
}
}
impl Error for LabelingError {}
impl From<LabelingError> for crate::MLError {
fn from(err: LabelingError) -> Self {
crate::MLError::InferenceError(err.to_string())
}
}
#[cfg(test)]
#[allow(
clippy::unnecessary_wraps,
clippy::manual_range_contains,
clippy::assertions_on_result_states
)]
mod tests {
use super::*;
use crate::MLError;
use tracing::info;
#[test]
fn test_gpu_traits() -> Result<(), MLError> {
// Test actual GPU availability instead of hardcoded platform checks
let gpu_available = GPULabelingEngine::gpu_available();
// The result depends on whether CUDA is actually available
// We don't assert a specific value since it depends on the test environment
info!("GPU available: {}", gpu_available);
// Optimal batch size should be reasonable regardless of GPU availability
let batch_size = GPULabelingEngine::optimal_batch_size();
assert!(
batch_size >= 1024 && batch_size <= 8192,
"Batch size {} should be reasonable",
batch_size
);
Ok(())
}
#[test]
fn test_gpu_labeling_engine_creation() {
let device = MlDevice::Cpu; // CPU fallback for tests without CUDA
let engine = GPULabelingEngine::new(device);
assert!(engine.is_ok());
}
#[test]
fn test_batch_processing() -> Result<(), Box<dyn Error>> {
let device = MlDevice::Cpu;
let engine = GPULabelingEngine::new(device)?;
let prices = vec![100.0, 101.0, 99.5];
let timestamps = vec![1000, 2000, 3000];
let result = engine.process_batch(&prices, &timestamps);
assert!(result.is_ok());
let labels = result?;
assert_eq!(labels.len(), 3);
Ok(())
}
}