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>
163 lines
4.8 KiB
Rust
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, ×tamps);
|
|
assert!(result.is_ok());
|
|
|
|
let labels = result?;
|
|
assert_eq!(labels.len(), 3);
|
|
Ok(())
|
|
}
|
|
}
|