use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct HardwareClass {
pub cpu_family: CpuFamily,
pub core_count_class: CoreClass,
pub cache_class: CacheClass,
}
impl HardwareClass {
#[must_use]
pub fn from_system() -> Self {
let cpu_count = num_cpus::get();
Self {
cpu_family: CpuFamily::detect(),
core_count_class: CoreClass::from_count(cpu_count),
cache_class: CacheClass::detect(),
}
}
#[must_use]
pub fn similarity(&self, other: &HardwareClass) -> f64 {
let mut score = 0.0;
if self.cpu_family == other.cpu_family {
score += 0.5;
} else if self.cpu_family.compatible_with(&other.cpu_family) {
score += 0.25;
}
let core_distance = self.core_count_class.distance(&other.core_count_class);
score += 0.3 * (1.0 - (core_distance as f64 / 4.0).min(1.0));
let cache_distance = self.cache_class.distance(&other.cache_class);
score += 0.2 * (1.0 - (cache_distance as f64 / 3.0).min(1.0));
score
}
#[must_use]
pub fn performance_factor(&self, baseline: &HardwareClass) -> f64 {
let core_factor = self.core_count_class.speedup() / baseline.core_count_class.speedup();
let cache_factor = 1.0 + (self.cache_class.mb() - baseline.cache_class.mb()) * 0.02;
core_factor * cache_factor
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CpuFamily {
IntelCore,
IntelXeon,
AmdRyzen,
AmdEpyc,
AppleSilicon,
ArmCortex,
Unknown,
}
impl CpuFamily {
#[must_use]
pub fn detect() -> Self {
#[cfg(target_arch = "x86_64")]
{
Self::IntelCore }
#[cfg(target_arch = "aarch64")]
{
Self::AppleSilicon }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
Self::Unknown
}
}
#[must_use]
pub fn compatible_with(&self, other: &CpuFamily) -> bool {
use CpuFamily::{IntelCore, IntelXeon, AmdRyzen, AmdEpyc, AppleSilicon, ArmCortex};
match (self, other) {
(IntelCore, IntelXeon) | (IntelXeon, IntelCore) => true,
(AmdRyzen, AmdEpyc) | (AmdEpyc, AmdRyzen) => true,
(AppleSilicon, ArmCortex) | (ArmCortex, AppleSilicon) => true,
_ => self == other,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CoreClass {
Single, Dual, Quad, Octa, Many, }
impl CoreClass {
#[must_use]
pub fn from_count(count: usize) -> Self {
match count {
1 => Self::Single,
2 => Self::Dual,
3..=4 => Self::Quad,
5..=8 => Self::Octa,
_ => Self::Many,
}
}
#[must_use]
pub fn distance(&self, other: &CoreClass) -> usize {
use CoreClass::{Single, Dual, Quad, Octa, Many};
let self_idx: usize = match self {
Single => 0,
Dual => 1,
Quad => 2,
Octa => 3,
Many => 4,
};
let other_idx: usize = match other {
Single => 0,
Dual => 1,
Quad => 2,
Octa => 3,
Many => 4,
};
self_idx.abs_diff(other_idx)
}
#[must_use]
pub fn speedup(&self) -> f64 {
match self {
Self::Single => 1.0,
Self::Dual => 1.8,
Self::Quad => 3.2,
Self::Octa => 5.5,
Self::Many => 8.0,
}
}
#[must_use]
pub fn representative_count(&self) -> usize {
match self {
Self::Single => 1,
Self::Dual => 2,
Self::Quad => 4,
Self::Octa => 8,
Self::Many => 16,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CacheClass {
Small, Medium, Large, Huge, }
impl CacheClass {
#[must_use]
pub fn detect() -> Self {
Self::Medium
}
#[must_use]
pub fn distance(&self, other: &CacheClass) -> usize {
use CacheClass::{Small, Medium, Large, Huge};
let self_idx: usize = match self {
Small => 0,
Medium => 1,
Large => 2,
Huge => 3,
};
let other_idx: usize = match other {
Small => 0,
Medium => 1,
Large => 2,
Huge => 3,
};
self_idx.abs_diff(other_idx)
}
#[must_use]
pub fn mb(&self) -> f64 {
match self {
Self::Small => 2.0,
Self::Medium => 6.0,
Self::Large => 12.0,
Self::Huge => 24.0,
}
}
}
impl fmt::Display for HardwareClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:?}/{:?}/{:?}",
self.cpu_family, self.core_count_class, self.cache_class
)
}
}
#[derive(Debug, Clone)]
pub struct HardwareProfile {
pub class: HardwareClass,
pub cpu_name: String,
pub physical_cores: usize,
pub logical_cores: usize,
pub cache_sizes: CacheSizes,
pub memory_gb: f64,
}
#[derive(Debug, Clone)]
pub struct CacheSizes {
pub l1_data_kb: u32,
pub l1_inst_kb: u32,
pub l2_kb: u32,
pub l3_kb: u32,
}
impl HardwareProfile {
#[must_use]
pub fn from_system() -> Self {
let logical_cores = num_cpus::get();
let physical_cores = num_cpus::get_physical();
Self {
class: HardwareClass::from_system(),
cpu_name: "Unknown CPU".to_string(), physical_cores,
logical_cores,
cache_sizes: CacheSizes {
l1_data_kb: 32,
l1_inst_kb: 32,
l2_kb: 256,
l3_kb: 8192,
},
memory_gb: 16.0, }
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}