use std::error::Error as StdError;
use std::fmt;
use std::sync::{Arc, Mutex};
use crate::error::{Error, PandRSError, Result};
use crate::lock_safe;
use crate::optimized::dataframe::OptimizedDataFrame;
use crate::DataFrame;
use crate::Series;
#[derive(Debug, Clone)]
pub struct GpuConfig {
pub enabled: bool,
pub memory_limit: usize,
pub device_id: i32,
pub fallback_to_cpu: bool,
pub use_pinned_memory: bool,
pub min_size_threshold: usize,
}
impl Default for GpuConfig {
fn default() -> Self {
GpuConfig {
enabled: true,
memory_limit: 1024 * 1024 * 1024, device_id: 0,
fallback_to_cpu: true,
use_pinned_memory: true,
min_size_threshold: 10_000, }
}
}
pub struct GpuContext {
device_status: Arc<Mutex<GpuDeviceStatus>>,
config: GpuConfig,
}
#[derive(Debug, Clone)]
pub struct GpuDeviceStatus {
pub available: bool,
pub cuda_version: Option<String>,
pub device_name: Option<String>,
pub total_memory: Option<usize>,
pub free_memory: Option<usize>,
pub core_count: Option<usize>,
}
impl Default for GpuDeviceStatus {
fn default() -> Self {
GpuDeviceStatus {
available: false,
cuda_version: None,
device_name: None,
total_memory: None,
free_memory: None,
core_count: None,
}
}
}
impl GpuContext {
pub fn new(config: GpuConfig) -> Self {
let device_status = Arc::new(Mutex::new(Self::detect_device(&config)));
GpuContext {
device_status,
config,
}
}
fn detect_device(_config: &GpuConfig) -> GpuDeviceStatus {
#[cfg(cuda_available)]
{
match cudarc::driver::CudaContext::new(0) {
Ok(_context) => {
return GpuDeviceStatus {
available: true,
cuda_version: Some("11.0+".to_string()), device_name: Some("CUDA Device".to_string()),
total_memory: None, free_memory: None, core_count: None, };
}
Err(_) => {
}
}
}
GpuDeviceStatus::default()
}
pub fn is_available(&self) -> bool {
if !self.config.enabled {
return false;
}
lock_safe!(self.device_status, "gpu device status lock")
.map(|status| status.available)
.unwrap_or(false)
}
pub fn get_device_status(&self) -> GpuDeviceStatus {
lock_safe!(self.device_status, "gpu device status lock")
.map(|status| status.clone())
.unwrap_or_default()
}
pub fn update_device_status(&self) {
let new_status = Self::detect_device(&self.config);
if let Ok(mut status) = lock_safe!(self.device_status, "gpu device status lock") {
*status = new_status;
}
}
pub fn should_use_gpu(&self, size: usize) -> bool {
self.is_available() && size >= self.config.min_size_threshold
}
pub fn config(&self) -> &GpuConfig {
&self.config
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GpuOperationType {
MatrixMultiply,
Add,
Subtract,
Multiply,
Divide,
Aggregate,
Sort,
Filter,
}
#[derive(Debug)]
pub enum GpuError {
DriverNotAvailable,
InsufficientMemory,
IncompatibleDataType,
DeviceError(String),
KernelExecutionError(String),
TransferError(String),
}
impl fmt::Display for GpuError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GpuError::DriverNotAvailable => write!(f, "CUDA driver not available"),
GpuError::InsufficientMemory => write!(f, "Insufficient GPU memory"),
GpuError::IncompatibleDataType => write!(f, "Incompatible data type for GPU operation"),
GpuError::DeviceError(msg) => write!(f, "GPU device error: {}", msg),
GpuError::KernelExecutionError(msg) => write!(f, "GPU kernel execution error: {}", msg),
GpuError::TransferError(msg) => write!(f, "GPU data transfer error: {}", msg),
}
}
}
impl StdError for GpuError {}
impl From<GpuError> for Error {
fn from(error: GpuError) -> Self {
Error::Computation(error.to_string())
}
}
#[derive(Clone)]
pub struct GpuManager {
context: Arc<GpuContext>,
}
impl GpuManager {
pub fn new() -> Self {
Self::with_config(GpuConfig::default())
}
pub fn with_config(config: GpuConfig) -> Self {
GpuManager {
context: Arc::new(GpuContext::new(config)),
}
}
pub fn context(&self) -> &GpuContext {
&self.context
}
pub fn is_available(&self) -> bool {
self.context.is_available()
}
pub fn device_info(&self) -> GpuDeviceStatus {
self.context.get_device_status()
}
}
lazy_static::lazy_static! {
static ref GPU_MANAGER: Mutex<Option<GpuManager>> = Mutex::new(None);
}
pub fn init_gpu() -> Result<GpuDeviceStatus> {
init_gpu_with_config(GpuConfig::default())
}
pub fn init_gpu_with_config(config: GpuConfig) -> Result<GpuDeviceStatus> {
let manager = GpuManager::with_config(config);
let status = manager.device_info();
let mut global = lock_safe!(GPU_MANAGER, "global gpu manager lock")?;
*global = Some(manager);
Ok(status)
}
pub fn get_gpu_manager() -> Result<GpuManager> {
let global = lock_safe!(GPU_MANAGER, "global gpu manager lock")?;
match &*global {
Some(manager) => Ok(manager.clone()),
None => {
drop(global); init_gpu()?;
get_gpu_manager()
}
}
}
#[cfg(cuda_available)]
pub mod cuda;
pub mod operations;
pub mod benchmark;
pub mod advanced_ops;
pub mod multi_gpu;
pub mod memory_pool;