const BAR_WIDTH: usize = 40;
const GIB: u64 = 1024 * 1024 * 1024;
fn gib(bytes: u64) -> String {
let tenths = bytes / (GIB / 10);
format!("{}.{} GiB", tenths / 10, tenths % 10)
}
fn bar(used: Option<u64>, total: u64) -> String {
let Some(used) = used.filter(|_| total > 0) else {
return format!("[{}] usage unknown", "·".repeat(BAR_WIDTH));
};
let used = used.min(total);
let scaled = u128::from(used) * BAR_WIDTH as u128 / u128::from(total);
let filled = usize::try_from(scaled).unwrap_or(BAR_WIDTH).min(BAR_WIDTH);
let tenths = u128::from(used) * 1000 / u128::from(total);
format!(
"[{}{}] {}.{}%",
"█".repeat(filled),
"░".repeat(BAR_WIDTH - filled),
tenths / 10,
tenths % 10,
)
}
fn main() {
let gpus = gpu_probe::detect();
println!("gpu-probe · {} GPU(s)", gpus.len());
println!();
if gpus.is_empty() {
println!(" no GPUs detected");
}
for (index, gpu) in gpus.iter().enumerate() {
println!("[{index}] {} · {}", gpu.name, gpu.vendor);
match gpu.arch_target {
Some(arch) => println!(" arch {:>10}", arch.to_string()),
None => println!(" arch unavailable"),
}
println!(" total {:>10}", gib(gpu.total_bytes));
match gpu.used_bytes {
Some(used) => println!(" used {:>10}", gib(used)),
None => println!(" used unknown"),
}
match gpu.free_bytes {
Some(free) => println!(" free {:>10}", gib(free)),
None => println!(" free unknown"),
}
println!(" {}", bar(gpu.used_bytes, gpu.total_bytes));
println!();
}
println!("host");
match gpu_probe::oneapi_host() {
Some(oneapi) => println!(" oneapi {:>10}", oneapi.version.to_string()),
None => println!(" oneapi unavailable"),
}
match gpu_probe::rocm_host() {
Some(rocm) => println!(" rocm {:>10}", rocm.version.to_string()),
None => println!(" rocm unavailable"),
}
match gpu_probe::cuda_host() {
Some(cuda) => println!(" cuda {:>10}", cuda.driver_version.to_string()),
None => println!(" cuda unavailable"),
}
match gpu_probe::vulkan_host() {
Some(vulkan) => println!(" vulkan {:>10}", vulkan.api_version.to_string()),
None => println!(" vulkan unavailable"),
}
}