use std::sync::OnceLock;
mod banner;
pub(crate) mod platform;
pub(crate) mod select;
mod tier;
pub(crate) mod thresholds;
pub use banner::startup_banner;
pub use select::{
gpu_could_engage, parse_backend_str, select_backend, select_backend_verdict,
BackendRoutingReason, BackendRoutingVerdict, BACKEND_OVERRIDE_VALUES,
};
pub use tier::{gpu_routing_profile, gpu_routing_profiles, GpuRoutingProfile};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ScanBackend {
GpuCuda,
GpuWgpu,
SimdCpu,
CpuFallback,
}
impl ScanBackend {
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::GpuCuda => "gpu-cuda-region-presence",
Self::GpuWgpu => "gpu-wgpu-region-presence",
Self::SimdCpu => "simd-regex",
Self::CpuFallback => "cpu-fallback",
}
}
#[must_use]
pub const fn is_gpu(self) -> bool {
matches!(self, Self::GpuCuda | Self::GpuWgpu)
}
}
#[must_use]
pub const fn multiple_backends_compiled() -> bool {
simd_backend_compiled() || gpu_backend_compiled()
}
#[must_use]
pub const fn simd_backend_compiled() -> bool {
cfg!(feature = "simd")
}
#[must_use]
pub fn hyperscan_runtime_identity() -> Option<String> {
#[cfg(feature = "simd")]
{
Some(hyperscan::version().to_string())
}
#[cfg(not(feature = "simd"))]
{
None
}
}
#[must_use]
pub const fn gpu_backend_compiled() -> bool {
cfg!(feature = "gpu")
}
#[must_use]
pub const fn simd_label(has_avx512: bool, has_avx2: bool, has_neon: bool) -> &'static str {
if has_avx512 {
"AVX-512"
} else if has_avx2 {
"AVX2"
} else if has_neon {
"NEON"
} else {
"scalar"
}
}
#[derive(Debug, Clone)]
pub struct HardwareCaps {
pub physical_cores: usize,
pub logical_cores: usize,
pub has_avx2: bool,
pub has_avx512: bool,
pub has_neon: bool,
pub gpu_available: bool,
pub gpu_name: Option<String>,
pub gpu_vram_mb: Option<u64>,
pub gpu_runtime_identity: Option<String>,
pub gpu_is_software: bool,
pub total_memory_mb: Option<u64>,
pub io_uring_available: bool,
pub hyperscan_available: bool,
pub hyperscan_runtime_identity: Option<String>,
}
static HW_PROBE: OnceLock<HardwareCaps> = OnceLock::new();
pub fn probe_hardware() -> &'static HardwareCaps {
HW_PROBE.get_or_init(|| {
let logical_cores = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1); let physical_cores = platform::physical_core_count().unwrap_or(logical_cores);
#[cfg(target_arch = "x86_64")]
let (has_avx2, has_avx512, has_neon) = (
std::arch::is_x86_feature_detected!("avx2"),
std::arch::is_x86_feature_detected!("avx512f"),
false,
);
#[cfg(target_arch = "aarch64")]
let (has_avx2, has_avx512, has_neon) = (false, false, true);
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
let (has_avx2, has_avx512, has_neon) = (false, false, false);
let gpu_probe = crate::gpu::gpu_probe();
let gpu_available = gpu_probe.available;
let gpu_name = gpu_probe.name;
let gpu_vram_mb = gpu_probe.buffer_limit_mb;
let gpu_runtime_identity = gpu_probe.runtime_identity;
let gpu_is_software = gpu_probe.is_software;
if gpu_is_software {
tracing::warn!(
gpu = ?gpu_name,
"Software GPU detected: GPU scanning disabled (slower than CPU)"
);
}
let hyperscan_available = cfg!(feature = "simd");
let hyperscan_runtime_identity = hyperscan_available
.then(hyperscan_runtime_identity)
.flatten();
let total_memory_mb = platform::detect_total_memory_mb();
let io_uring_available = platform::detect_io_uring();
let caps = HardwareCaps {
physical_cores,
logical_cores,
has_avx2,
has_avx512,
has_neon,
gpu_available,
gpu_name: gpu_name.clone(),
gpu_vram_mb,
gpu_runtime_identity,
gpu_is_software,
total_memory_mb,
io_uring_available,
hyperscan_available,
hyperscan_runtime_identity,
};
tracing::info!(
physical_cores,
logical_cores,
gpu_available,
gpu_name = ?gpu_name,
has_avx512 = caps.has_avx512,
has_avx2 = caps.has_avx2,
has_neon = caps.has_neon,
hyperscan = hyperscan_available,
io_uring = io_uring_available,
"hardware probe complete"
);
caps
})
}
#[cfg(test)]
#[doc(hidden)]
pub mod testing {
pub use super::{
gpu_could_engage, parse_backend_str, probe_hardware, select_backend,
select_backend_verdict, startup_banner, BackendRoutingReason, BackendRoutingVerdict,
HardwareCaps, ScanBackend,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpuTier {
High,
Mid,
Low,
}
fn from_inner(tier: super::tier::GpuTier) -> GpuTier {
match tier {
super::tier::GpuTier::High => GpuTier::High,
super::tier::GpuTier::Mid => GpuTier::Mid,
super::tier::GpuTier::Low => GpuTier::Low,
}
}
fn to_inner(tier: GpuTier) -> super::tier::GpuTier {
match tier {
GpuTier::High => super::tier::GpuTier::High,
GpuTier::Mid => super::tier::GpuTier::Mid,
GpuTier::Low => super::tier::GpuTier::Low,
}
}
pub fn cpu_tier_backend(caps: &HardwareCaps) -> ScanBackend {
super::select::cpu_tier_backend(caps)
}
pub fn classify_gpu_tier(adapter_name: Option<&str>) -> GpuTier {
from_inner(super::tier::classify_gpu_tier(adapter_name))
}
pub fn gpu_min_bytes_for_tier(tier: GpuTier) -> u64 {
super::tier::gpu_min_bytes_for_tier(to_inner(tier))
}
pub fn gpu_solo_bytes_for_tier(tier: GpuTier) -> u64 {
super::tier::gpu_solo_bytes_for_tier(to_inner(tier))
}
pub fn gpu_pattern_breakeven_for_tier(tier: GpuTier) -> usize {
super::tier::gpu_pattern_breakeven_for_tier(to_inner(tier))
}
pub fn select_backend_for_batch(
caps: &HardwareCaps,
workload_bytes: u64,
pattern_count: usize,
large_chunk_bytes: u64,
) -> ScanBackend {
super::select::select_backend_for_batch(
caps,
workload_bytes,
pattern_count,
large_chunk_bytes,
)
}
pub fn select_backend_for_batch_verdict(
caps: &HardwareCaps,
workload_bytes: u64,
pattern_count: usize,
large_chunk_bytes: u64,
) -> BackendRoutingVerdict {
super::select::select_backend_for_batch_verdict(
caps,
workload_bytes,
pattern_count,
large_chunk_bytes,
)
}
pub fn forced_backend_override_for_test() -> Option<ScanBackend> {
super::select::forced_backend_override_for_test()
}
#[cfg(target_os = "linux")]
pub fn linux_physical_cores_from_cpuinfo(content: &str) -> Option<usize> {
super::platform::linux_physical_cores_from_cpuinfo(content)
}
#[cfg(target_os = "linux")]
pub fn linux_total_memory_mb_from_meminfo(content: &str) -> Option<u64> {
super::platform::linux_total_memory_mb_from_meminfo(content)
}
}