use crate::error::Result;
use blueprint_core::info;
use std::path::Path;
use std::process::{Command, Stdio};
use super::{BenchmarkRunConfig, GpuBenchmarkResult};
pub const DEFAULT_UNKNOWN_GPU_MEMORY: f32 = 512.0;
pub const DEFAULT_INTEL_GPU_MEMORY: f32 = 1024.0;
pub const DEFAULT_NVIDIA_GPU_MEMORY: f32 = 2048.0;
pub const DEFAULT_AMD_GPU_MEMORY: f32 = 2048.0;
pub fn run_gpu_benchmark(_config: &BenchmarkRunConfig) -> Result<GpuBenchmarkResult> {
info!("Running GPU benchmark");
if let Ok(output) = Command::new("nvidia-smi")
.args([
"--query-gpu=memory.total,name,clocks.max.graphics",
"--format=csv,noheader,nounits",
])
.output()
{
if output.status.success() {
if let Ok(output_str) = String::from_utf8(output.stdout) {
let output_str = output_str.trim();
if !output_str.is_empty() {
let parts: Vec<&str> = output_str.split(',').collect();
if parts.len() >= 3 {
if let Ok(memory) = parts[0].trim().parse::<f32>() {
let gpu_model = parts[1].trim().to_string();
let gpu_frequency_mhz = parts[2].trim().parse::<f32>().unwrap_or(0.0);
info!(
"Detected NVIDIA GPU: {}, {} MHz with {} MB memory",
gpu_model, gpu_frequency_mhz, memory
);
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: memory,
gpu_model,
gpu_frequency_mhz,
});
}
}
}
}
}
}
if let Ok(output) = Command::new("rocm-smi")
.args(["--showmeminfo", "vram", "--showproductname"])
.output()
{
if output.status.success() {
if let Ok(output_str) = String::from_utf8(output.stdout) {
let memory = parse_amd_gpu_memory(&output_str);
let gpu_model = parse_amd_gpu_model(&output_str);
if memory > 0.0 {
info!("Detected AMD GPU: {}, with {} MB memory", gpu_model, memory);
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: memory,
gpu_model,
gpu_frequency_mhz: 0.0, });
}
}
}
}
if let Ok(output) = Command::new("intel_gpu_top")
.args(["-l"])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.and_then(|mut child| {
std::thread::sleep(std::time::Duration::from_millis(100));
child.kill()?;
child.wait_with_output()
})
{
if let Ok(output_str) = String::from_utf8(output.stdout) {
let memory = parse_intel_gpu_memory(&output_str);
let (gpu_model, gpu_frequency_mhz) = parse_intel_gpu_info(&output_str);
if memory > 0.0 {
info!(
"Detected Intel GPU: {}, {} MHz with {} MB memory",
gpu_model, gpu_frequency_mhz, memory
);
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: memory,
gpu_model,
gpu_frequency_mhz,
});
}
}
}
if let Ok(output) = Command::new("glxinfo").args(["-B"]).output() {
if output.status.success() {
if let Ok(output_str) = String::from_utf8(output.stdout) {
if output_str.contains("OpenGL renderer string:") {
let renderer_line = output_str
.lines()
.find(|line| line.contains("OpenGL renderer string:"))
.unwrap_or("");
let gpu_model = extract_gpu_model_from_glxinfo(renderer_line);
let memory = parse_glxinfo_memory(&output_str);
let gpu_memory = if memory > 0.0 {
info!("Detected GPU with {} MB memory via glxinfo", memory);
memory
} else if renderer_line.contains("NVIDIA") {
info!(
"Detected NVIDIA GPU via glxinfo, assuming {} MB memory",
DEFAULT_NVIDIA_GPU_MEMORY
);
DEFAULT_NVIDIA_GPU_MEMORY
} else if renderer_line.contains("AMD") || renderer_line.contains("ATI") {
info!(
"Detected AMD GPU via glxinfo, assuming {} MB memory",
DEFAULT_AMD_GPU_MEMORY
);
DEFAULT_AMD_GPU_MEMORY
} else if renderer_line.contains("Intel") {
info!(
"Detected Intel GPU via glxinfo, assuming {} MB memory",
DEFAULT_INTEL_GPU_MEMORY
);
DEFAULT_INTEL_GPU_MEMORY
} else {
info!(
"Detected unknown GPU via glxinfo, assuming {} MB memory",
DEFAULT_UNKNOWN_GPU_MEMORY
);
DEFAULT_UNKNOWN_GPU_MEMORY
};
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: gpu_memory,
gpu_model,
gpu_frequency_mhz: 0.0, });
}
}
}
}
let nvidia_device = Path::new("/dev/nvidia0");
let amdgpu_device = Path::new("/dev/dri/renderD128");
if nvidia_device.exists() {
info!(
"Detected NVIDIA GPU device file, assuming {} MB memory",
DEFAULT_NVIDIA_GPU_MEMORY
);
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: DEFAULT_NVIDIA_GPU_MEMORY,
gpu_model: "NVIDIA GPU (detected via device file)".to_string(),
gpu_frequency_mhz: 0.0,
});
} else if amdgpu_device.exists() {
info!(
"Detected AMD GPU device file, assuming {} MB memory",
DEFAULT_AMD_GPU_MEMORY
);
return Ok(GpuBenchmarkResult {
gpu_available: true,
gpu_memory_mb: DEFAULT_AMD_GPU_MEMORY,
gpu_model: "AMD GPU (detected via device file)".to_string(),
gpu_frequency_mhz: 0.0,
});
}
info!("No GPU detected");
Ok(GpuBenchmarkResult {
gpu_available: false,
gpu_memory_mb: 0.0,
gpu_model: "No GPU detected".to_string(),
gpu_frequency_mhz: 0.0,
})
}
fn extract_gpu_model_from_glxinfo(renderer_line: &str) -> String {
if renderer_line.contains("OpenGL renderer string:") {
let parts: Vec<&str> = renderer_line.split(':').collect();
if parts.len() >= 2 {
return parts[1].trim().to_string();
}
}
"Unknown GPU".to_string()
}
pub fn parse_amd_gpu_model(output: &str) -> String {
for line in output.lines() {
if line.contains("GPU") && line.contains("Product name") {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 2 {
return parts[1].trim().to_string();
}
}
}
"AMD GPU".to_string()
}
pub fn parse_intel_gpu_info(output: &str) -> (String, f32) {
let mut model = "Intel GPU".to_string();
let mut frequency = 0.0;
for line in output.lines() {
if line.contains("Device:") {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 2 {
model = parts[1].trim().to_string();
}
}
if let Some(freq_str) = line.split_whitespace().next() {
if freq_str.ends_with("MHz") {
if let Ok(freq) = freq_str.trim_end_matches("MHz").parse::<f32>() {
frequency = freq;
}
}
}
}
(model, frequency)
}
pub fn parse_amd_gpu_memory(output: &str) -> f32 {
for line in output.lines() {
if line.trim().starts_with("0") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
if let Ok(memory) = parts[1].parse::<f32>() {
return memory;
}
}
}
}
0.0
}
pub fn parse_intel_gpu_memory(output: &str) -> f32 {
for line in output.lines() {
if line.contains("VRAM") {
if let Some(memory_info) = output.lines().find(|l| l.contains("Memory: ")) {
if let Some(memory_str) = memory_info.split("Memory: ").nth(1) {
if let Some(memory_val) = memory_str.split_whitespace().next() {
if let Ok(memory) = memory_val.parse::<f32>() {
return memory;
}
}
}
}
return DEFAULT_INTEL_GPU_MEMORY;
}
}
0.0
}
pub fn parse_glxinfo_memory(output: &str) -> f32 {
for line in output.lines() {
if line.contains("Video memory:") {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 2 {
let memory_part = parts[1].trim();
if memory_part.ends_with("MB") {
if let Ok(memory) = memory_part.trim_end_matches("MB").trim().parse::<f32>() {
return memory;
}
} else if memory_part.ends_with("GB") {
if let Ok(memory) = memory_part.trim_end_matches("GB").trim().parse::<f32>() {
return memory * 1024.0; }
}
}
}
}
for line in output.lines() {
if line.contains("memory:") {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 2 {
let memory_part = parts[1].trim();
if memory_part.ends_with("MB") {
if let Ok(memory) = memory_part.trim_end_matches("MB").trim().parse::<f32>() {
return memory;
}
} else if memory_part.ends_with("GB") {
if let Ok(memory) = memory_part.trim_end_matches("GB").trim().parse::<f32>() {
return memory * 1024.0; }
}
}
}
}
0.0
}