use std::sync::OnceLock;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct HardwareDetector {
cpu_info: CpuInfo,
memory_info: MemoryInfo,
cache_info: CacheInfo,
}
#[derive(Debug, Clone)]
pub struct CpuInfo {
pub cores: usize,
pub logical_cores: usize,
pub architecture: String,
pub vendor: String,
pub model_name: String,
pub base_frequency_mhz: Option<u32>,
pub max_frequency_mhz: Option<u32>,
pub features: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct MemoryInfo {
pub total_memory_bytes: u64,
pub available_memory_bytes: u64,
pub page_size_bytes: usize,
}
#[derive(Debug, Clone, Default)]
pub struct CacheInfo {
pub l1_data_cache_kb: Option<u32>,
pub l1_instruction_cache_kb: Option<u32>,
pub l2_cache_kb: Option<u32>,
pub l3_cache_kb: Option<u32>,
pub cache_line_size_bytes: Option<u32>,
}
impl Default for CpuInfo {
fn default() -> Self {
Self {
cores: 1,
logical_cores: 1,
architecture: "unknown".to_string(),
vendor: "unknown".to_string(),
model_name: "unknown".to_string(),
base_frequency_mhz: None,
max_frequency_mhz: None,
features: Vec::new(),
}
}
}
impl Default for MemoryInfo {
fn default() -> Self {
Self {
total_memory_bytes: 0,
available_memory_bytes: 0,
page_size_bytes: 4096, }
}
}
impl HardwareDetector {
pub fn new() -> Self {
Self {
cpu_info: Self::detect_cpu_info(),
memory_info: Self::detect_memory_info(),
cache_info: Self::detect_cache_info(),
}
}
pub fn cpu_cores(&self) -> usize {
self.cpu_info.cores
}
pub fn logical_cores(&self) -> usize {
self.cpu_info.logical_cores
}
pub fn cpu_architecture(&self) -> &str {
&self.cpu_info.architecture
}
pub fn cpu_vendor(&self) -> &str {
&self.cpu_info.vendor
}
pub fn total_memory(&self) -> u64 {
self.memory_info.total_memory_bytes
}
pub fn available_memory(&self) -> u64 {
self.memory_info.available_memory_bytes
}
pub fn has_avx2(&self) -> bool {
self.cpu_info.features.iter().any(|f| f.contains("avx2"))
}
pub fn has_avx512(&self) -> bool {
self.cpu_info.features.iter().any(|f| f.contains("avx512"))
}
pub fn has_sse42(&self) -> bool {
self.cpu_info.features.iter().any(|f| f.contains("sse4_2"))
}
pub fn has_neon(&self) -> bool {
self.cpu_info.features.iter().any(|f| f.contains("neon"))
}
pub fn is_arm(&self) -> bool {
self.cpu_info.architecture.contains("arm") || self.cpu_info.architecture.contains("aarch")
}
pub fn is_x86(&self) -> bool {
self.cpu_info.architecture.contains("x86") || self.cpu_info.architecture.contains("x64")
}
pub fn l3_cache_size_kb(&self) -> Option<u32> {
self.cache_info.l3_cache_kb
}
pub fn cache_line_size(&self) -> Option<u32> {
self.cache_info.cache_line_size_bytes
}
pub fn cpu_features(&self) -> &[String] {
&self.cpu_info.features
}
fn detect_cpu_info() -> CpuInfo {
let logical_cores = num_cpus::get();
let raw_physical = num_cpus::get_physical();
let cores = raw_physical.min(logical_cores);
let mut cpu_info = CpuInfo {
logical_cores,
cores,
architecture: std::env::consts::ARCH.to_string(),
..Default::default()
};
#[cfg(target_arch = "x86_64")]
{
Self::detect_x86_features(&mut cpu_info);
}
#[cfg(target_arch = "aarch64")]
{
Self::detect_arm_features(&mut cpu_info);
}
cpu_info
}
#[cfg(target_arch = "x86_64")]
fn detect_x86_features(cpu_info: &mut CpuInfo) {
if is_x86_feature_detected!("sse") {
cpu_info.features.push("sse".to_string());
}
if is_x86_feature_detected!("sse2") {
cpu_info.features.push("sse2".to_string());
}
if is_x86_feature_detected!("sse3") {
cpu_info.features.push("sse3".to_string());
}
if is_x86_feature_detected!("sse4.1") {
cpu_info.features.push("sse4_1".to_string());
}
if is_x86_feature_detected!("sse4.2") {
cpu_info.features.push("sse4_2".to_string());
}
if is_x86_feature_detected!("avx") {
cpu_info.features.push("avx".to_string());
}
if is_x86_feature_detected!("avx2") {
cpu_info.features.push("avx2".to_string());
}
if is_x86_feature_detected!("fma") {
cpu_info.features.push("fma".to_string());
}
cpu_info.vendor = "Intel/AMD".to_string(); }
#[cfg(target_arch = "aarch64")]
fn detect_arm_features(cpu_info: &mut CpuInfo) {
cpu_info.vendor = "ARM".to_string();
cpu_info.features.push("neon".to_string()); }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn detect_x86_features(_cpu_info: &mut CpuInfo) {}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
fn detect_arm_features(_cpu_info: &mut CpuInfo) {}
fn detect_memory_info() -> MemoryInfo {
let mut memory_info = MemoryInfo::default();
#[cfg(feature = "sysinfo")]
{
use sysinfo::System;
let mut system = System::new_all();
system.refresh_memory();
memory_info.total_memory_bytes = system.total_memory() * 1024; memory_info.available_memory_bytes = system.available_memory() * 1024;
}
#[cfg(not(feature = "sysinfo"))]
{
#[cfg(target_os = "linux")]
{
if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
for line in meminfo.lines() {
if line.starts_with("MemTotal:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
memory_info.total_memory_bytes = kb * 1024;
}
}
} else if line.starts_with("MemAvailable:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
memory_info.available_memory_bytes = kb * 1024;
}
}
}
}
}
}
#[cfg(target_os = "macos")]
{
if let Ok(output) = std::process::Command::new("sysctl")
.arg("-n")
.arg("hw.memsize")
.output()
{
if let Ok(mem_str) = String::from_utf8(output.stdout) {
if let Ok(mem_bytes) = mem_str.trim().parse::<u64>() {
memory_info.total_memory_bytes = mem_bytes;
memory_info.available_memory_bytes = mem_bytes / 2; }
}
}
}
if memory_info.total_memory_bytes == 0 {
memory_info.total_memory_bytes = 8 * 1024 * 1024 * 1024; memory_info.available_memory_bytes = 4 * 1024 * 1024 * 1024; }
}
#[cfg(unix)]
{
unsafe {
let page_size = libc::sysconf(libc::_SC_PAGESIZE);
if page_size > 0 {
memory_info.page_size_bytes = page_size as usize;
}
}
}
memory_info
}
fn detect_cache_info() -> CacheInfo {
let mut cache_info = CacheInfo::default();
#[cfg(target_os = "linux")]
{
Self::detect_linux_cache_info(&mut cache_info);
}
#[cfg(target_os = "macos")]
{
Self::detect_macos_cache_info(&mut cache_info);
}
#[cfg(target_os = "windows")]
{
Self::detect_windows_cache_info(&mut cache_info);
}
cache_info
}
#[cfg(target_os = "linux")]
fn detect_linux_cache_info(cache_info: &mut CacheInfo) {
if let Ok(l1d) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
if let Ok(size) = l1d.trim().strip_suffix("K").unwrap_or(&l1d).parse::<u32>() {
cache_info.l1_data_cache_kb = Some(size);
}
}
if let Ok(l2) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
if let Ok(size) = l2.trim().strip_suffix("K").unwrap_or(&l2).parse::<u32>() {
cache_info.l2_cache_kb = Some(size);
}
}
if let Ok(l3) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index3/size") {
if let Ok(size) = l3.trim().strip_suffix("K").unwrap_or(&l3).parse::<u32>() {
cache_info.l3_cache_kb = Some(size);
}
}
cache_info.cache_line_size_bytes = Some(64);
}
#[cfg(target_os = "macos")]
fn detect_macos_cache_info(cache_info: &mut CacheInfo) {
cache_info.cache_line_size_bytes = Some(64); }
#[cfg(target_os = "windows")]
fn detect_windows_cache_info(cache_info: &mut CacheInfo) {
cache_info.cache_line_size_bytes = Some(64); }
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
fn detect_linux_cache_info(_cache_info: &mut CacheInfo) {}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
fn detect_macos_cache_info(_cache_info: &mut CacheInfo) {}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
fn detect_windows_cache_info(_cache_info: &mut CacheInfo) {}
}
impl Default for HardwareDetector {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct OSInfo {
pub name: String,
pub version: String,
pub arch: String,
pub kernel_version: Option<String>,
pub is_64bit: bool,
}
impl OSInfo {
pub fn detect() -> Self {
Self {
name: std::env::consts::OS.to_string(),
version: Self::detect_os_version(),
arch: std::env::consts::ARCH.to_string(),
kernel_version: Self::detect_kernel_version(),
is_64bit: cfg!(target_pointer_width = "64"),
}
}
pub fn is_linux(&self) -> bool {
self.name == "linux"
}
pub fn is_macos(&self) -> bool {
self.name == "macos"
}
pub fn is_windows(&self) -> bool {
self.name == "windows"
}
pub fn is_unix(&self) -> bool {
cfg!(unix)
}
fn detect_os_version() -> String {
#[cfg(target_os = "linux")]
{
if let Ok(version) = std::fs::read_to_string("/proc/version") {
return version.lines().next().unwrap_or("unknown").to_string();
}
}
#[cfg(target_os = "macos")]
{
"macOS".to_string()
}
#[cfg(target_os = "windows")]
{
"Windows".to_string()
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
{
"unknown".to_string()
}
}
fn detect_kernel_version() -> Option<String> {
#[cfg(unix)]
{
if let Ok(output) = std::process::Command::new("uname").arg("-r").output() {
if let Ok(version) = String::from_utf8(output.stdout) {
return Some(version.trim().to_string());
}
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct RuntimeInfo {
pub rust_version: String,
pub compiler: String,
pub target_triple: String,
pub build_profile: String,
pub features: Vec<String>,
}
impl RuntimeInfo {
pub fn detect() -> Self {
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY")
.unwrap_or_else(|_| std::env::consts::FAMILY.to_string());
let target_triple = std::env::var("CARGO_CFG_TARGET_TRIPLE").unwrap_or_else(|_| {
format!(
"{}-{}-{}",
std::env::consts::ARCH,
std::env::consts::FAMILY,
std::env::consts::OS
)
});
Self {
rust_version: option_env!("CARGO_PKG_RUST_VERSION")
.unwrap_or("unknown")
.to_string(),
compiler: format!("{target_family} {}", rustc_version_runtime::version()),
target_triple,
build_profile: if cfg!(debug_assertions) {
"debug"
} else {
"release"
}
.to_string(),
features: Self::detect_features(),
}
}
pub fn is_debug(&self) -> bool {
self.build_profile == "debug"
}
pub fn is_release(&self) -> bool {
self.build_profile == "release"
}
fn detect_features() -> Vec<String> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct PerformanceCharacteristics {
pub memory_bandwidth_gbps: f64,
pub l1_cache_latency_ns: f64,
pub l2_cache_latency_ns: f64,
pub l3_cache_latency_ns: f64,
pub memory_latency_ns: f64,
pub cpu_frequency_estimation_mhz: f64,
pub single_thread_performance_score: f64,
pub multi_thread_performance_score: f64,
}
impl PerformanceCharacteristics {
pub fn measure() -> Self {
let _start_time = Instant::now();
let memory_bandwidth = Self::measure_memory_bandwidth();
let cache_latencies = Self::measure_cache_latencies();
let cpu_freq = Self::estimate_cpu_frequency();
let single_perf = Self::measure_single_thread_performance();
let multi_perf = Self::measure_multi_thread_performance();
Self {
memory_bandwidth_gbps: memory_bandwidth,
l1_cache_latency_ns: cache_latencies.0,
l2_cache_latency_ns: cache_latencies.1,
l3_cache_latency_ns: cache_latencies.2,
memory_latency_ns: cache_latencies.3,
cpu_frequency_estimation_mhz: cpu_freq,
single_thread_performance_score: single_perf,
multi_thread_performance_score: multi_perf,
}
}
fn measure_memory_bandwidth() -> f64 {
const SIZE: usize = 16 * 1024 * 1024; let mut data = vec![0u64; SIZE / 8];
let start = Instant::now();
for (i, item) in data.iter_mut().enumerate() {
*item = i as u64;
}
let mut sum = 0u64;
for &val in &data {
sum = sum.wrapping_add(val);
}
let elapsed = start.elapsed();
let bytes_processed = (SIZE * 2) as f64; let bandwidth_bps = bytes_processed / elapsed.as_secs_f64();
std::hint::black_box(sum);
bandwidth_bps / 1e9 }
fn measure_cache_latencies() -> (f64, f64, f64, f64) {
(1.0, 3.0, 10.0, 100.0) }
fn estimate_cpu_frequency() -> f64 {
let iterations = 10_000_000;
let start = Instant::now();
let mut counter = 0u64;
for _ in 0..iterations {
counter = counter.wrapping_add(1);
}
let elapsed = start.elapsed();
std::hint::black_box(counter);
let cycles_per_second = iterations as f64 / elapsed.as_secs_f64();
cycles_per_second / 1e6 }
fn measure_single_thread_performance() -> f64 {
let start = Instant::now();
let mut sum = 0.0;
for i in 0..1_000_000 {
sum += (i as f64).sqrt().sin().cos();
}
let elapsed = start.elapsed();
std::hint::black_box(sum);
1_000_000.0 / elapsed.as_secs_f64()
}
fn measure_multi_thread_performance() -> f64 {
use std::thread;
let num_threads = num_cpus::get();
let start = Instant::now();
let handles: Vec<_> = (0..num_threads)
.map(|_| {
thread::spawn(|| {
let mut sum = 0.0;
for i in 0..100_000 {
sum += (i as f64).sqrt().sin().cos();
}
sum
})
})
.collect();
let results: Vec<_> = handles
.into_iter()
.map(|h| h.join().expect("operation should succeed"))
.collect();
let elapsed = start.elapsed();
std::hint::black_box(results);
(num_threads * 100_000) as f64 / elapsed.as_secs_f64()
}
}
#[derive(Debug, Clone)]
pub struct FeatureChecker {
hardware: HardwareDetector,
os_info: OSInfo,
#[allow(dead_code)]
runtime: RuntimeInfo,
}
impl FeatureChecker {
pub fn new() -> Self {
Self {
hardware: HardwareDetector::new(),
os_info: OSInfo::detect(),
runtime: RuntimeInfo::detect(),
}
}
pub fn simd_available(&self) -> bool {
self.hardware.has_sse42() || self.hardware.has_avx2() || self.hardware.has_neon()
}
pub fn parallel_beneficial(&self) -> bool {
self.hardware.logical_cores() > 1
}
pub fn memory_mapping_available(&self) -> bool {
self.os_info.is_unix() || self.os_info.is_windows()
}
pub fn high_resolution_timer_available(&self) -> bool {
true }
pub fn recommended_thread_count(&self) -> usize {
self.hardware.logical_cores().min(32)
}
pub fn recommended_simd_width(&self) -> usize {
if self.hardware.has_avx512() {
16 } else if self.hardware.has_avx2() {
8 } else if self.hardware.has_sse42() || self.hardware.has_neon() {
4 } else {
1 }
}
pub fn optimization_recommended(&self, optimization: &str) -> bool {
match optimization {
"simd" => self.simd_available(),
"parallel" => self.parallel_beneficial(),
"cache_friendly" => self.hardware.l3_cache_size_kb().is_some(),
"memory_pool" => self.hardware.total_memory() > 1_000_000_000, _ => false,
}
}
pub fn get_recommendations(&self) -> Vec<String> {
let mut recommendations = Vec::new();
if self.simd_available() {
recommendations.push("Enable SIMD optimizations".to_string());
}
if self.parallel_beneficial() {
recommendations.push(format!(
"Use parallel processing with {} threads",
self.recommended_thread_count()
));
}
if let Some(cache_size) = self.hardware.l3_cache_size_kb() {
recommendations.push(format!("Optimize for {cache_size}KB L3 cache"));
}
if self.hardware.total_memory() < 1_000_000_000 {
recommendations.push("Consider memory usage optimizations".to_string());
}
recommendations
}
}
impl Default for FeatureChecker {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_ENV_INFO: OnceLock<EnvironmentInfo> = OnceLock::new();
#[derive(Debug, Clone)]
pub struct EnvironmentInfo {
pub hardware: HardwareDetector,
pub os_info: OSInfo,
pub runtime: RuntimeInfo,
pub performance: PerformanceCharacteristics,
pub features: FeatureChecker,
}
impl EnvironmentInfo {
pub fn global() -> &'static EnvironmentInfo {
GLOBAL_ENV_INFO.get_or_init(EnvironmentInfo::detect)
}
pub fn detect() -> Self {
let hardware = HardwareDetector::new();
let os_info = OSInfo::detect();
let runtime = RuntimeInfo::detect();
let performance = PerformanceCharacteristics::measure();
let features = FeatureChecker::new();
Self {
hardware,
os_info,
runtime,
performance,
features,
}
}
pub fn summary(&self) -> String {
format!(
"Environment Summary:\n\
OS: {} {} ({})\n\
CPU: {} cores ({} logical), {}\n\
Memory: {:.1} GB total, {:.1} GB available\n\
Runtime: {} {}\n\
Features: SIMD={}, Parallel={}, Cache={}KB",
self.os_info.name,
self.os_info.version,
self.os_info.arch,
self.hardware.cpu_cores(),
self.hardware.logical_cores(),
self.hardware.cpu_architecture(),
self.hardware.total_memory() as f64 / 1e9,
self.hardware.available_memory() as f64 / 1e9,
self.runtime.compiler,
self.runtime.build_profile,
self.features.simd_available(),
self.features.parallel_beneficial(),
self.hardware.l3_cache_size_kb().unwrap_or(0)
)
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hardware_detector() {
let hw = HardwareDetector::new();
assert!(hw.cpu_cores() >= 1);
assert!(hw.logical_cores() >= 1); assert!(!hw.cpu_architecture().is_empty());
assert!(hw.total_memory() > 0);
}
#[test]
fn test_os_info() {
let os = OSInfo::detect();
assert!(!os.name.is_empty());
assert!(!os.arch.is_empty());
assert!(os.is_linux() || os.is_macos() || os.is_windows() || !os.name.is_empty());
}
#[test]
fn test_runtime_info() {
let runtime = RuntimeInfo::detect();
assert!(!runtime.compiler.is_empty());
assert!(!runtime.target_triple.is_empty());
assert!(runtime.is_debug() || runtime.is_release());
}
#[test]
fn test_performance_characteristics() {
let perf = PerformanceCharacteristics::measure();
assert!(perf.memory_bandwidth_gbps > 0.0);
assert!(perf.cpu_frequency_estimation_mhz > 0.0);
assert!(perf.single_thread_performance_score > 0.0);
assert!(perf.multi_thread_performance_score > 0.0);
}
#[test]
fn test_feature_checker() {
let checker = FeatureChecker::new();
assert!(checker.recommended_thread_count() >= 1);
assert!(checker.recommended_simd_width() >= 1);
assert!(checker.high_resolution_timer_available());
}
#[test]
fn test_environment_info() {
let env = EnvironmentInfo::detect();
let summary = env.summary();
assert!(!summary.is_empty());
assert!(summary.contains("Environment Summary"));
let global_env = EnvironmentInfo::global();
assert!(!global_env.summary().is_empty());
}
#[test]
fn test_cpu_features() {
let hw = HardwareDetector::new();
let _has_avx2 = hw.has_avx2();
let _has_sse42 = hw.has_sse42();
let _has_neon = hw.has_neon();
let _is_arm = hw.is_arm();
let _is_x86 = hw.is_x86();
}
#[test]
fn test_optimization_recommendations() {
let checker = FeatureChecker::new();
let recommendations = checker.get_recommendations();
assert!(!recommendations.is_empty());
let _simd_rec = checker.optimization_recommended("simd");
let _parallel_rec = checker.optimization_recommended("parallel");
let _cache_rec = checker.optimization_recommended("cache_friendly");
}
}