use crate::errors::{Result, TrustformersError};
use crate::kernels::intel_kernels::{
IntelDevice, IntelKernel, IntelKernelConfig, IntelPrecision, IntelUtils,
};
use crate::tensor::Tensor;
use std::sync::{Arc, Mutex, OnceLock};
pub struct IntelImpl {
kernel_manager: Arc<Mutex<IntelKernel>>,
device: IntelDevice,
available_devices: Vec<IntelDevice>,
stats: Arc<Mutex<IntelStats>>,
}
#[derive(Debug, Clone, Default)]
pub struct IntelStats {
pub total_operations: u64,
pub total_time_us: u64,
pub memory_h2d_bytes: u64,
pub memory_d2h_bytes: u64,
pub compilation_time_us: u64,
pub kernel_launches: u64,
}
static INTEL_INSTANCE: OnceLock<std::result::Result<Arc<IntelImpl>, String>> = OnceLock::new();
impl IntelImpl {
pub fn new() -> Result<Self> {
let available_devices = IntelUtils::detect_devices()?;
if available_devices.is_empty() {
return Err(TrustformersError::hardware_error(
"No Intel GPU devices found",
"intel_device_detection",
));
}
let device = available_devices[0].clone();
let config = IntelKernelConfig {
device_id: device.id,
workgroup_size: IntelUtils::get_optimal_workgroup_size(1024, device.max_workgroup_size),
preferred_workgroup_size_multiple: if device.sub_group_sizes.contains(&32) {
32
} else {
16
},
max_workgroup_size: device.max_workgroup_size,
local_memory_size: device.local_memory_size,
global_memory_size: device.global_memory_size,
compute_units: device.compute_units,
max_clock_frequency: device.max_clock_frequency,
sub_group_size: device.sub_group_sizes[0],
enable_profiling: true,
enable_fp16: device.supports_fp16,
enable_dpas: device.supports_dpas,
};
let kernel_manager = IntelKernel::new(config).map_err(|e| {
TrustformersError::hardware_error(
&format!("Failed to initialize Intel kernels: {}", e),
"intel_kernel_init",
)
})?;
Ok(Self {
kernel_manager: Arc::new(Mutex::new(kernel_manager)),
device,
available_devices,
stats: Arc::new(Mutex::new(IntelStats::default())),
})
}
pub fn global() -> Result<&'static Arc<IntelImpl>> {
let slot =
INTEL_INSTANCE.get_or_init(|| Self::new().map(Arc::new).map_err(|e| e.to_string()));
match slot {
Ok(instance) => Ok(instance),
Err(message) => Err(TrustformersError::hardware_error(
message,
"intel_global_init",
)),
}
}
pub fn is_available() -> bool {
IntelUtils::detect_devices().map(|devices| !devices.is_empty()).unwrap_or(false)
}
pub fn matmul(&self, a: &Tensor, b: &Tensor, c: &mut Tensor) -> Result<()> {
let start_time = std::time::Instant::now();
let mut kernel_manager =
self.kernel_manager.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let precision = IntelUtils::get_recommended_precision(&self.device);
let result = kernel_manager.gemm(a, b, c, 1.0, 0.0, precision);
let elapsed = start_time.elapsed();
let mut stats = self.stats.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
stats.total_operations += 1;
stats.total_time_us += elapsed.as_micros() as u64;
stats.kernel_launches += 1;
result
}
pub fn flash_attention(
&self,
query: &Tensor,
key: &Tensor,
value: &Tensor,
output: &mut Tensor,
) -> Result<()> {
let start_time = std::time::Instant::now();
let mut kernel_manager =
self.kernel_manager.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let precision = IntelUtils::get_recommended_precision(&self.device);
let head_dim = query.shape().last().copied().unwrap_or(64) as f32;
let scale = 1.0 / head_dim.sqrt();
let result = kernel_manager.attention(query, key, value, output, scale, precision);
let elapsed = start_time.elapsed();
let mut stats = self.stats.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
stats.total_operations += 1;
stats.total_time_us += elapsed.as_micros() as u64;
stats.kernel_launches += 1;
result
}
pub fn layer_norm(
&self,
input: &Tensor,
weight: &Tensor,
bias: Option<&Tensor>,
output: &mut Tensor,
eps: f32,
) -> Result<()> {
let start_time = std::time::Instant::now();
let mut kernel_manager =
self.kernel_manager.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let precision = IntelUtils::get_recommended_precision(&self.device);
let result = kernel_manager.layer_norm(input, weight, bias, output, eps, precision);
let elapsed = start_time.elapsed();
let mut stats = self.stats.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
stats.total_operations += 1;
stats.total_time_us += elapsed.as_micros() as u64;
stats.kernel_launches += 1;
result
}
pub fn device_info(&self) -> String {
format!(
"Intel {} (Driver: {}, Compute Units: {}, Memory: {:.1} GB, FP16: {}, DPAS: {})",
self.device.name,
self.device.driver_version,
self.device.compute_units,
self.device.global_memory_size as f64 / (1024.0 * 1024.0 * 1024.0),
self.device.supports_fp16,
self.device.supports_dpas
)
}
pub fn memory_stats(&self) -> Result<(usize, usize)> {
let kernel_manager =
self.kernel_manager.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
let memory_stats = kernel_manager.memory_stats()?;
Ok((memory_stats.total_allocated, self.device.global_memory_size))
}
pub fn get_stats(&self) -> IntelStats {
self.stats.lock().unwrap_or_else(|poisoned| poisoned.into_inner()).clone()
}
pub fn reset_stats(&self) {
let mut stats = self.stats.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
*stats = IntelStats::default();
}
pub fn list_devices(&self) -> &[IntelDevice] {
&self.available_devices
}
pub fn current_device(&self) -> &IntelDevice {
&self.device
}
pub fn has_xmx_support(&self) -> bool {
IntelUtils::has_xmx_support(&self.device)
}
pub fn recommended_precision(&self) -> IntelPrecision {
IntelUtils::get_recommended_precision(&self.device)
}
}
pub mod api {
use super::*;
pub fn init_intel() -> Result<()> {
IntelImpl::global()?;
Ok(())
}
pub fn is_intel_available() -> bool {
IntelImpl::is_available()
}
pub fn intel_matmul(a: &Tensor, b: &Tensor, c: &mut Tensor) -> Result<()> {
let intel = IntelImpl::global()?;
intel.matmul(a, b, c)
}
pub fn intel_flash_attention(
query: &Tensor,
key: &Tensor,
value: &Tensor,
output: &mut Tensor,
) -> Result<()> {
let intel = IntelImpl::global()?;
intel.flash_attention(query, key, value, output)
}
pub fn intel_layer_norm(
input: &Tensor,
weight: &Tensor,
bias: Option<&Tensor>,
output: &mut Tensor,
eps: f32,
) -> Result<()> {
let intel = IntelImpl::global()?;
intel.layer_norm(input, weight, bias, output, eps)
}
pub fn intel_device_info() -> Result<String> {
let intel = IntelImpl::global()?;
Ok(intel.device_info())
}
pub fn intel_memory_stats() -> Result<(usize, usize)> {
let intel = IntelImpl::global()?;
intel.memory_stats()
}
pub fn intel_performance_stats() -> Result<IntelStats> {
let intel = IntelImpl::global()?;
Ok(intel.get_stats())
}
pub fn intel_reset_stats() -> Result<()> {
let intel = IntelImpl::global()?;
intel.reset_stats();
Ok(())
}
pub fn intel_list_devices() -> Result<Vec<IntelDevice>> {
let intel = IntelImpl::global()?;
Ok(intel.list_devices().to_vec())
}
pub fn intel_has_xmx() -> Result<bool> {
let intel = IntelImpl::global()?;
Ok(intel.has_xmx_support())
}
pub fn intel_recommended_precision() -> Result<IntelPrecision> {
let intel = IntelImpl::global()?;
Ok(intel.recommended_precision())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernels::intel_kernels::IntelUtils;
use crate::tensor::Tensor;
#[test]
fn test_detect_devices_reports_no_phantom_devices() {
let devices = IntelUtils::detect_devices().expect("detect_devices should not error");
assert!(
devices.is_empty(),
"must not report a fabricated device when no real Intel GPU runtime is wired up"
);
}
#[test]
fn test_intel_initialization_honestly_fails_without_real_hardware() {
let result = api::init_intel();
assert!(
result.is_err(),
"must not fabricate a fallback device when no real Intel GPU is present"
);
}
#[test]
fn test_is_intel_available_is_false_without_real_hardware() {
assert!(!api::is_intel_available());
}
#[test]
fn test_intel_matmul_propagates_honest_error() {
let a = Tensor::ones(&[4, 4]).expect("Failed to create ones tensor");
let b = Tensor::ones(&[4, 4]).expect("Failed to create ones tensor");
let mut c = Tensor::zeros(&[4, 4]).expect("Failed to create zero tensor");
let result = api::intel_matmul(&a, &b, &mut c);
assert!(result.is_err());
}
#[test]
fn test_intel_list_devices_propagates_honest_error() {
assert!(api::intel_list_devices().is_err());
}
}